aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/AST/Decl.h77
-rw-r--r--include/clang/AST/Expr.h8
-rw-r--r--include/clang/Basic/DiagnosticASTKinds.td2
-rw-r--r--lib/AST/ASTImporter.cpp5
-rw-r--r--lib/AST/Decl.cpp89
-rw-r--r--lib/AST/ExprConstant.cpp119
-rw-r--r--lib/Sema/SemaDecl.cpp40
-rw-r--r--lib/Serialization/ASTReaderDecl.cpp8
-rw-r--r--lib/Serialization/ASTWriterDecl.cpp7
-rw-r--r--test/CXX/basic/basic.types/p10.cpp9
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp21
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp14
-rw-r--r--test/CXX/expr/expr.const/p2-0x.cpp10
-rw-r--r--test/PCH/cxx-constexpr.cpp19
-rw-r--r--test/SemaCXX/constant-expression-cxx11.cpp67
-rw-r--r--test/SemaCXX/constant-expression.cpp13
-rw-r--r--test/SemaCXX/constexpr-depth.cpp6
-rw-r--r--test/SemaCXX/constexpr-printing.cpp9
-rw-r--r--test/SemaCXX/cxx0x-class.cpp4
-rw-r--r--test/SemaCXX/i-c-e-cxx.cpp4
20 files changed, 314 insertions, 217 deletions
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index 92a6a5469c..0a24b72560 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -664,8 +664,9 @@ struct EvaluatedStmt {
/// integral constant expression.
bool CheckingICE : 1;
- /// \brief Whether this statement is an integral constant
- /// expression. Only valid if CheckedICE is true.
+ /// \brief Whether this statement is an integral constant expression,
+ /// or in C++11, whether the statement is a constant expression. Only
+ /// valid if CheckedICE is true.
bool IsICE : 1;
Stmt *Value;
@@ -1019,41 +1020,13 @@ public:
/// \endcode
bool extendsLifetimeOfTemporary() const;
- EvaluatedStmt *EnsureEvaluatedStmt() const {
- EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>();
- if (!Eval) {
- Stmt *S = Init.get<Stmt *>();
- Eval = new (getASTContext()) EvaluatedStmt;
- Eval->Value = S;
- Init = Eval;
- }
- return Eval;
- }
-
- /// \brief Check whether we are in the process of checking whether the
- /// initializer can be evaluated.
- bool isEvaluatingValue() const {
- if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>())
- return Eval->IsEvaluating;
-
- return false;
- }
+ EvaluatedStmt *ensureEvaluatedStmt() const;
- /// \brief Note that we now are checking whether the initializer can be
- /// evaluated.
- void setEvaluatingValue() const {
- EvaluatedStmt *Eval = EnsureEvaluatedStmt();
- Eval->IsEvaluating = true;
- }
-
- /// \brief Note that constant evaluation has computed the given
- /// value for this variable's initializer.
- void setEvaluatedValue(const APValue &Value) const {
- EvaluatedStmt *Eval = EnsureEvaluatedStmt();
- Eval->IsEvaluating = false;
- Eval->WasEvaluated = true;
- Eval->Evaluated = Value;
- }
+ /// \brief Attempt to evaluate the value of the initializer attached to this
+ /// declaration, and produce notes explaining why it cannot be evaluated or is
+ /// not a constant expression. Returns true if evaluation succeeded.
+ /// The value can be obtained by calling getEvaluatedValue.
+ bool evaluateValue(llvm::SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
/// \brief Return the already-evaluated value of this variable's
/// initializer, or NULL if the value is not yet known. Returns pointer
@@ -1075,8 +1048,9 @@ public:
return false;
}
- /// \brief Determines whether the initializer is an integral
- /// constant expression.
+ /// \brief Determines whether the initializer is an integral constant
+ /// expression, or in C++11, whether the initializer is a constant
+ /// expression.
///
/// \pre isInitKnownICE()
bool isInitICE() const {
@@ -1085,30 +1059,9 @@ public:
return Init.get<EvaluatedStmt *>()->IsICE;
}
- /// \brief Check whether we are in the process of checking the initializer
- /// is an integral constant expression.
- bool isCheckingICE() const {
- if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>())
- return Eval->CheckingICE;
-
- return false;
- }
-
- /// \brief Note that we now are checking whether the initializer is an
- /// integral constant expression.
- void setCheckingICE() const {
- EvaluatedStmt *Eval = EnsureEvaluatedStmt();
- Eval->CheckingICE = true;
- }
-
- /// \brief Note that we now know whether the initializer is an
- /// integral constant expression.
- void setInitKnownICE(bool IsICE) const {
- EvaluatedStmt *Eval = EnsureEvaluatedStmt();
- Eval->CheckingICE = false;
- Eval->CheckedICE = true;
- Eval->IsICE = IsICE;
- }
+ /// \brief Determine whether the value of the initializer attached to this
+ /// declaration is an integral constant expression.
+ bool checkInitIsICE() const;
void setCXXDirectInitializer(bool T) { VarDeclBits.HasCXXDirectInit = T; }
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h
index ce541f63db..9c86c16c4e 100644
--- a/include/clang/AST/Expr.h
+++ b/include/clang/AST/Expr.h
@@ -495,6 +495,14 @@ public:
/// lvalue with link time known address, with no side-effects.
bool EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx) const;
+ /// EvaluateAsInitializer - Evaluate an expression as if it were the
+ /// initializer of the given declaration. Returns true if the initializer
+ /// can be folded to a constant, and produces any relevant notes. In C++11,
+ /// notes will be produced if the expression is not a constant expression.
+ bool EvaluateAsInitializer(APValue &Result, const ASTContext &Ctx,
+ const VarDecl *VD,
+ llvm::SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
+
/// \brief Enumeration used to describe the kind of Null pointer constant
/// returned from \c isNullPointerConstant().
enum NullPointerConstantKind {
diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td
index ae6b59ca9d..8f0b4938ad 100644
--- a/include/clang/Basic/DiagnosticASTKinds.td
+++ b/include/clang/Basic/DiagnosticASTKinds.td
@@ -30,6 +30,8 @@ def note_constexpr_non_global : Note<
def note_constexpr_past_end : Note<
"dereferenced pointer past the end of %select{|subobject of}0 "
"%select{temporary|%2}1 is not a constant expression">;
+def note_constexpr_var_init_non_constant : Note<
+ "initializer of %0 is not a constant expression">;
def note_constexpr_temporary_here : Note<"temporary created here">;
def note_constexpr_depth_limit_exceeded : Note<
"constexpr evaluation exceeded maximum depth of %0 calls">;
diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp
index 963643e542..a95c941e02 100644
--- a/lib/AST/ASTImporter.cpp
+++ b/lib/AST/ASTImporter.cpp
@@ -2802,6 +2802,11 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
} else {
Expr *Init = Importer.Import(DDef->getInit());
MergeWithVar->setInit(Init);
+ if (DDef->isInitKnownICE()) {
+ EvaluatedStmt *Eval = MergeWithVar->ensureEvaluatedStmt();
+ Eval->CheckedICE = true;
+ Eval->IsICE = DDef->isInitICE();
+ }
}
}
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 34eefc0dcc..305c41c51c 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -1337,6 +1337,94 @@ void VarDecl::setInit(Expr *I) {
Init = I;
}
+/// Convert the initializer for this declaration to the elaborated EvaluatedStmt
+/// form, which contains extra information on the evaluated value of the
+/// initializer.
+EvaluatedStmt *VarDecl::ensureEvaluatedStmt() const {
+ EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>();
+ if (!Eval) {
+ Stmt *S = Init.get<Stmt *>();
+ Eval = new (getASTContext()) EvaluatedStmt;
+ Eval->Value = S;
+ Init = Eval;
+ }
+ return Eval;
+}
+
+bool VarDecl::evaluateValue(
+ llvm::SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
+ EvaluatedStmt *Eval = ensureEvaluatedStmt();
+
+ // We only produce notes indicating why an initializer is non-constant the
+ // first time it is evaluated. FIXME: The notes won't always be emitted the
+ // first time we try evaluation, so might not be produced at all.
+ if (Eval->WasEvaluated)
+ return !Eval->Evaluated.isUninit();
+
+ const Expr *Init = cast<Expr>(Eval->Value);
+ assert(!Init->isValueDependent());
+
+ if (Eval->IsEvaluating) {
+ // FIXME: Produce a diagnostic for self-initialization.
+ Eval->CheckedICE = true;
+ Eval->IsICE = false;
+ return false;
+ }
+
+ Eval->IsEvaluating = true;
+
+ bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, getASTContext(),
+ this, Notes);
+
+ // Ensure the result is an uninitialized APValue if evaluation fails.
+ if (!Result)
+ Eval->Evaluated = APValue();
+
+ Eval->IsEvaluating = false;
+ Eval->WasEvaluated = true;
+
+ // In C++11, we have determined whether the initializer was a constant
+ // expression as a side-effect.
+ if (getASTContext().getLangOptions().CPlusPlus0x && !Eval->CheckedICE) {
+ Eval->CheckedICE = true;
+ Eval->IsICE = Notes.empty();
+ }
+
+ return Result;
+}
+
+bool VarDecl::checkInitIsICE() const {
+ EvaluatedStmt *Eval = ensureEvaluatedStmt();
+ if (Eval->CheckedICE)
+ // We have already checked whether this subexpression is an
+ // integral constant expression.
+ return Eval->IsICE;
+
+ const Expr *Init = cast<Expr>(Eval->Value);
+ assert(!Init->isValueDependent());
+
+ // In C++11, evaluate the initializer to check whether it's a constant
+ // expression.
+ if (getASTContext().getLangOptions().CPlusPlus0x) {
+ llvm::SmallVector<PartialDiagnosticAt, 8> Notes;
+ evaluateValue(Notes);
+ return Eval->IsICE;
+ }
+
+ // It's an ICE whether or not the definition we found is
+ // out-of-line. See DR 721 and the discussion in Clang PR
+ // 6206 for details.
+
+ if (Eval->CheckingICE)
+ return false;
+ Eval->CheckingICE = true;
+
+ Eval->IsICE = Init->isIntegerConstantExpr(getASTContext());
+ Eval->CheckingICE = false;
+ Eval->CheckedICE = true;
+ return Eval->IsICE;
+}
+
bool VarDecl::extendsLifetimeOfTemporary() const {
assert(getType()->isReferenceType() &&"Non-references never extend lifetime");
@@ -2687,4 +2775,3 @@ SourceRange ImportDecl::getSourceRange() const {
return SourceRange(getLocation(), getIdentifierLocs().back());
}
-
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index 88a033151a..ff556c3094 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -382,6 +382,14 @@ namespace {
return OptionalDiagnostic();
return OptionalDiagnostic(&addDiag(Loc, DiagId));
}
+
+ /// Add a stack of notes to a prior diagnostic.
+ void addNotes(ArrayRef<PartialDiagnosticAt> Diags) {
+ if (HasActiveDiagnostic) {
+ EvalStatus.Diag->insert(EvalStatus.Diag->end(),
+ Diags.begin(), Diags.end());
+ }
+ }
};
}
@@ -1069,6 +1077,13 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,
return true;
}
+ // Dig out the initializer, and use the declaration which it's attached to.
+ const Expr *Init = VD->getAnyInitializer(VD);
+ if (!Init || Init->isValueDependent()) {
+ Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
+ return false;
+ }
+
// If we're currently evaluating the initializer of this declaration, use that
// in-flight value.
if (Info.EvaluatingDecl == VD) {
@@ -1083,47 +1098,23 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,
return false;
}
- const Expr *Init = VD->getAnyInitializer();
- if (!Init || Init->isValueDependent()) {
- Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
- return false;
- }
-
- if (APValue *V = VD->getEvaluatedValue()) {
- Result = CCValue(*V, CCValue::GlobalValue());
- return !Result.isUninit();
- }
-
- if (VD->isEvaluatingValue()) {
- Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
- return false;
- }
-
- VD->setEvaluatingValue();
-
- Expr::EvalStatus EStatus;
- EvalInfo InitInfo(Info.Ctx, EStatus);
- APValue EvalResult;
- InitInfo.setEvaluatingDecl(VD, EvalResult);
- LValue LVal;
- LVal.set(VD);
- // FIXME: The caller will need to know whether the value was a constant
- // expression. If not, we should propagate up a diagnostic.
- if (!EvaluateConstantExpression(EvalResult, InitInfo, LVal, Init)) {
- // FIXME: If the evaluation failure was not permanent (for instance, if we
- // hit a variable with no declaration yet, or a constexpr function with no
- // definition yet), the standard is unclear as to how we should behave.
- //
- // Either the initializer should be evaluated when the variable is defined,
- // or a failed evaluation of the initializer should be reattempted each time
- // it is used.
- VD->setEvaluatedValue(APValue());
- Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
+ // Check that we can fold the initializer. In C++, we will have already done
+ // this in the cases where it matters for conformance.
+ llvm::SmallVector<PartialDiagnosticAt, 8> Notes;
+ if (!VD->evaluateValue(Notes)) {
+ Info.Diag(E->getExprLoc(), diag::note_constexpr_var_init_non_constant,
+ Notes.size() + 1) << VD;
+ Info.Note(VD->getLocation(), diag::note_declared_at);
+ Info.addNotes(Notes);
return false;
+ } else if (!VD->checkInitIsICE()) {
+ Info.CCEDiag(E->getExprLoc(), diag::note_constexpr_var_init_non_constant,
+ Notes.size() + 1) << VD;
+ Info.Note(VD->getLocation(), diag::note_declared_at);
+ Info.addNotes(Notes);
}
- VD->setEvaluatedValue(EvalResult);
- Result = CCValue(EvalResult, CCValue::GlobalValue());
+ Result = CCValue(*VD->getEvaluatedValue(), CCValue::GlobalValue());
return true;
}
@@ -1523,6 +1514,8 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
if (Info.getLangOpts().CPlusPlus0x) {
const FunctionDecl *DiagDecl = Definition ? Definition : Declaration;
+ // FIXME: If DiagDecl is an implicitly-declared special member function, we
+ // should be much more explicit about why it's not constexpr.
Info.Diag(CallLoc, diag::note_constexpr_invalid_function, 1)
<< DiagDecl->isConstexpr() << isa<CXXConstructorDecl>(DiagDecl)
<< DiagDecl;
@@ -4887,6 +4880,22 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx) const {
CCEK_Constant);
}
+bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
+ const VarDecl *VD,
+ llvm::SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
+ Expr::EvalStatus EStatus;
+ EStatus.Diag = &Notes;
+
+ EvalInfo InitInfo(Ctx, EStatus);
+ InitInfo.setEvaluatingDecl(VD, Value);
+
+ LValue LVal;
+ LVal.set(VD);
+
+ return EvaluateConstantExpression(Value, InitInfo, LVal, this) &&
+ !EStatus.HasSideEffects;
+}
+
/// isEvaluatable - Call EvaluateAsRValue to see if this expression can be
/// constant folded, but discard the result.
bool Expr::isEvaluatable(const ASTContext &Ctx) const {
@@ -5083,33 +5092,13 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) {
if (!Dcl->getType()->isIntegralOrEnumerationType())
return ICEDiag(2, cast<DeclRefExpr>(E)->getLocation());
- // Look for a declaration of this variable that has an initializer.
- const VarDecl *ID = 0;
- const Expr *Init = Dcl->getAnyInitializer(ID);
- if (Init) {
- if (ID->isInitKnownICE()) {
- // We have already checked whether this subexpression is an
- // integral constant expression.
- if (ID->isInitICE())
- return NoDiag();
- else
- return ICEDiag(2, cast<DeclRefExpr>(E)->getLocation());
- }
-
- // It's an ICE whether or not the definition we found is
- // out-of-line. See DR 721 and the discussion in Clang PR
- // 6206 for details.
-
- if (Dcl->isCheckingICE()) {
- return ICEDiag(2, cast<DeclRefExpr>(E)->getLocation());
- }
-
- Dcl->setCheckingICE();
- ICEDiag Result = CheckICE(Init, Ctx);
- // Cache the result of the ICE test.
- Dcl->setInitKnownICE(Result.Val == 0);
- return Result;
- }
+ const VarDecl *VD;
+ // Look for a declaration of this variable that has an initializer, and
+ // check whether it is an ICE.
+ if (Dcl->getAnyInitializer(VD) && VD->checkInitIsICE())
+ return NoDiag();
+ else
+ return ICEDiag(2, cast<DeclRefExpr>(E)->getLocation());
}
}
return ICEDiag(2, E->getLocStart());
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index a0291afeed..c31850313a 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -6539,17 +6539,39 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
Expr *Init = var->getInit();
bool IsGlobal = var->hasGlobalStorage() && !var->isStaticLocal();
- if (!var->getDeclContext()->isDependentContext() &&
- (var->isConstexpr() || IsGlobal) && Init &&
- !Init->isConstantInitializer(Context, baseType->isReferenceType())) {
- // FIXME: Improve this diagnostic to explain why the initializer is not
- // a constant expression.
- if (var->isConstexpr())
- Diag(var->getLocation(), diag::err_constexpr_var_requires_const_init)
- << var << Init->getSourceRange();
- if (IsGlobal)
+ if (!var->getDeclContext()->isDependentContext() && Init) {
+ if (IsGlobal && !var->isConstexpr() &&
+ getDiagnostics().getDiagnosticLevel(diag::warn_global_constructor,
+ var->getLocation())
+ != DiagnosticsEngine::Ignored &&
+ !Init->isConstantInitializer(Context, baseType->isReferenceType()))
Diag(var->getLocation(), diag::warn_global_constructor)
<< Init->getSourceRange();
+
+ QualType Type = var->getType();
+ if (var->isConstexpr()) {
+ llvm::SmallVector<PartialDiagnosticAt, 8> Notes;
+ if (!var->evaluateValue(Notes) || !var->isInitICE()) {
+ SourceLocation DiagLoc = var->getLocation();
+ // If the note doesn't add any useful information other than a source
+ // location, fold it into the primary diagnostic.
+ if (Notes.size() == 1 && Notes[0].second.getDiagID() ==
+ diag::note_invalid_subexpr_in_const_expr) {
+ DiagLoc = Notes[0].first;
+ Notes.clear();
+ }
+ Diag(DiagLoc, diag::err_constexpr_var_requires_const_init)
+ << var << Init->getSourceRange();
+ for (unsigned I = 0, N = Notes.size(); I != N; ++I)
+ Diag(Notes[I].first, Notes[I].second);
+ }
+ } else if (getLangOptions().CPlusPlus && !Type.isVolatileQualified() &&
+ Type.isConstQualified() && Type->isIntegralOrEnumerationType()) {
+ // Check whether the initializer of a const variable of integral or
+ // enumeration type is an ICE now, since we can't tell whether it was
+ // initialized by a constant expression if we check later.
+ var->checkInitIsICE();
+ }
}
// Require the destructor.
diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp
index 69f309aef0..3bcafe9d59 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -799,8 +799,14 @@ void ASTDeclReader::VisitVarDecl(VarDecl *VD) {
VD->VarDeclBits.NRVOVariable = Record[Idx++];
VD->VarDeclBits.CXXForRangeDecl = Record[Idx++];
VD->VarDeclBits.ARCPseudoStrong = Record[Idx++];
- if (Record[Idx++])
+ if (uint64_t Val = Record[Idx++]) {
VD->setInit(Reader.ReadExpr(F));
+ if (Val > 1) {
+ EvaluatedStmt *Eval = VD->ensureEvaluatedStmt();
+ Eval->CheckedICE = true;
+ Eval->IsICE = Val == 3;
+ }
+ }
if (Record[Idx++]) { // HasMemberSpecializationInfo.
VarDecl *Tmpl = ReadDeclAs<VarDecl>(Record, Idx);
diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp
index 6c6428d55a..26dfd36f77 100644
--- a/lib/Serialization/ASTWriterDecl.cpp
+++ b/lib/Serialization/ASTWriterDecl.cpp
@@ -704,9 +704,12 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
Record.push_back(D->isNRVOVariable());
Record.push_back(D->isCXXForRangeDecl());
Record.push_back(D->isARCPseudoStrong());
- Record.push_back(D->getInit() ? 1 : 0);
- if (D->getInit())
+ if (D->getInit()) {
+ Record.push_back(!D->isInitKnownICE() ? 1 : (D->isInitICE() ? 3 : 2));
Writer.AddStmt(D->getInit());
+ } else {
+ Record.push_back(0);
+ }
MemberSpecializationInfo *SpecInfo
= D->isStaticDataMember() ? D->getMemberSpecializationInfo() : 0;
diff --git a/test/CXX/basic/basic.types/p10.cpp b/test/CXX/basic/basic.types/p10.cpp
index 1f66e5522d..725be09c08 100644
--- a/test/CXX/basic/basic.types/p10.cpp
+++ b/test/CXX/basic/basic.types/p10.cpp
@@ -34,13 +34,12 @@ constexpr int f(DerivedFromNonTrivDtor<NonTrivDtorBase>); // expected-error {{co
struct TrivDtor {
constexpr TrivDtor();
};
-// FIXME: when building DefinitionData we look at 'isUserProvided' before it's set up!
-#if 0
+constexpr int f(TrivDtor);
struct TrivDefaultedDtor {
constexpr TrivDefaultedDtor();
~TrivDefaultedDtor() = default;
};
-#endif
+constexpr int f(TrivDefaultedDtor);
// - it is an aggregate type or has at least one constexpr constructor or
// constexpr constructor template that is not a copy or move constructor
@@ -100,6 +99,7 @@ struct ArrGood {
Agg agg[24];
double d[12];
TrivDtor td[3];
+ TrivDefaultedDtor tdd[3];
};
constexpr int f(ArrGood);
@@ -120,8 +120,7 @@ namespace MutableMembers {
// Here's one reason why allowing this would be a disaster...
template<int n> struct Id { int k = n; };
int f() {
- // FIXME: correctly check whether the initializer is a constant expression.
- constexpr MM m = { 0 }; // desired-error {{must be a constant expression}}
+ constexpr MM m = { 0 }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-literal type 'const MutableMembers::MM' cannot be used in a constant expression}}
++m.n;
return Id<m.n>().k; // expected-error {{not an integral constant expression}}
}
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 3f6f160221..0fb6f7def0 100644
--- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp
@@ -20,7 +20,7 @@ constexpr int s1::mi2 = 0;
// not a definition of an object
constexpr extern int i2; // expected-error {{constexpr variable declaration must be a definition}}
// not a literal type
-constexpr notlit nl1; // expected-error {{constexpr variable 'nl1' must be initialized by a constant expression}}
+constexpr notlit nl1; // expected-error {{constexpr variable 'nl1' must be initialized by a constant expression}} expected-note {{non-literal type 'const notlit' cannot be used in a constant expression}}
// function parameters
void f2(constexpr int i) {} // expected-error {{function parameter cannot be constexpr}}
// non-static member
@@ -42,12 +42,13 @@ 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();
+// FIXME: Mark default constructors as 'constexpr' when appropriate.
+constexpr class C3 {} c3 = C3(); // unexpected-error {{must be initialized by a constant expression}} unexpected-note {{non-constexpr constructor}} unexpected-note {{here}}
+constexpr struct S3 {} s3 = S3(); // unexpected-error {{must be initialized by a constant expression}} unexpected-note {{non-constexpr constructor}} unexpected-note {{here}}
constexpr union U3 {} u3 = {};
constexpr enum E3 { V3 } e3 = V3;
-class C4 {} constexpr c4 = C4();
-struct S4 {} constexpr s4 = S4();
+class C4 {} constexpr c4 = C4(); // unexpected-error {{must be initialized by a constant expression}} unexpected-note {{non-constexpr constructor}} unexpected-note {{here}}
+struct S4 {} constexpr s4 = S4(); // unexpected-error {{must be initialized by a constant expression}} unexpected-note {{non-constexpr constructor}} unexpected-note {{here}}
union U4 {} constexpr u4 = {};
enum E4 { V4 } constexpr e4 = V4;
constexpr int; // expected-error {{constexpr can only be used in variable and function declarations}}
@@ -67,7 +68,7 @@ struct ConstexprDtor {
};
// template stuff
-template <typename T> constexpr T ft(T t) { return t; }
+template <typename T> constexpr T ft(T t) { return t; } // unexpected-note {{here}}
template <typename T> T gt(T t) { return t; }
struct S {
template<typename T> constexpr T f();
@@ -89,7 +90,7 @@ template <> char S::g() { return 0; } // expected-error {{no function template m
template <> double S::g() const { return 0; } // ok
// FIXME: The initializer is a constant expression.
-constexpr int i3 = ft(1); // unexpected-error {{must be initialized by a constant expression}}
+constexpr int i3 = ft(1); // unexpected-error {{must be initialized by a constant expression}} unexpected-note {{undefined function 'ft<int>'}}
void test() {
// ignore constexpr when instantiating with non-literal
@@ -98,7 +99,7 @@ void test() {
}
// Examples from the standard:
-constexpr int square(int x);
+constexpr int square(int x); // expected-note {{declared here}}
constexpr int bufsz = 1024;
constexpr struct pixel { // expected-error {{struct cannot be marked constexpr}}
@@ -108,10 +109,10 @@ constexpr struct pixel { // expected-error {{struct cannot be marked constexpr}}
};
constexpr pixel::pixel(int a)
- : x(square(a)), y(square(a))
+ : x(square(a)), y(square(a)) // expected-note {{undefined function 'square' cannot be used in a constant expression}}
{ }
-constexpr pixel small(2); // expected-error {{must be initialized by a constant expression}}
+constexpr pixel small(2); // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'pixel(2)'}}
constexpr int square(int x) {
return x * x;
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp
index fe79a0e6fc..4145e9b224 100644
--- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp
@@ -18,17 +18,17 @@ extern int (*const d)(int);
// A variable declaration which uses the constexpr specifier shall have an
// initializer and shall be initialized by a constant expression.
constexpr int ni1; // expected-error {{default initialization of an object of const type 'const int'}}
-constexpr struct C { C(); } ni2; // expected-error {{constexpr variable 'ni2' must be initialized by a constant expression}}
+constexpr struct C { C(); } ni2; // expected-error {{constexpr variable 'ni2' must be initialized by a constant expression}} expected-note {{non-literal type 'const struct C' cannot be used in a constant expression}}
constexpr double &ni3; // expected-error {{declaration of reference variable 'ni3' requires an initializer}}
constexpr int nc1 = i; // expected-error {{constexpr variable 'nc1' must be initialized by a constant expression}}
-constexpr C nc2 = C(); // expected-error {{constexpr variable 'nc2' must be initialized by a constant expression}}
-int &f();
-constexpr int &nc3 = f(); // expected-error {{constexpr variable 'nc3' must be initialized by a constant expression}}
+constexpr C nc2 = C(); // expected-error {{constexpr variable 'nc2' must be initialized by a constant expression}} expected-note {{non-literal type}}
+int &f(); // expected-note {{declared here}}
+constexpr int &nc3 = f(); // expected-error {{constexpr variable 'nc3' must be initialized by a constant expression}} expected-note {{non-constexpr function 'f' cannot be used in a constant expression}}
constexpr int nc4(i); // expected-error {{constexpr variable 'nc4' must be initialized by a constant expression}}
-constexpr C nc5((C())); // expected-error {{constexpr variable 'nc5' must be initialized by a constant expression}}
-int &f();
-constexpr int &nc6(f()); // expected-error {{constexpr variable 'nc6' must be initialized by a constant expression}}
+constexpr C nc5((C())); // expected-error {{constexpr variable 'nc5' must be initialized by a constant expression}} expected-note {{non-literal type 'const C'}}
+int &f(); // expected-note {{here}}
+constexpr int &nc6(f()); // expected-error {{constexpr variable 'nc6' must be initialized by a constant expression}} expected-note {{non-constexpr function 'f'}}
struct pixel {
int x, y;
diff --git a/test/CXX/expr/expr.const/p2-0x.cpp b/test/CXX/expr/expr.const/p2-0x.cpp
index d02db92a26..aa83fc7fa9 100644
--- a/test/CXX/expr/expr.const/p2-0x.cpp
+++ b/test/CXX/expr/expr.const/p2-0x.cpp
@@ -78,13 +78,13 @@ namespace NonConstExprReturn {
namespace NonConstExprCtor {
struct T {
constexpr T(const int &r) :
- r(r) { // expected-note {{reference to temporary cannot be used to initialize a member in a constant expression}}
+ r(r) { // expected-note 2{{reference to temporary cannot be used to initialize a member in a constant expression}}
}
const int &r;
};
constexpr int n = 0;
constexpr T t1(n); // ok
- constexpr T t2(0); // expected-error {{must be initialized by a constant expression}}
+ constexpr T t2(0); // expected-error {{must be initialized by a constant expression}} expected-note {{temporary created here}} expected-note {{in call to 'T(0)'}}
struct S {
int n : T(4).r; // expected-error {{constant expression}} expected-note {{temporary created here}} expected-note {{in call to 'T(4)'}}
@@ -187,8 +187,8 @@ namespace References {
constexpr int e = 42;
int &f = const_cast<int&>(e);
extern int &g;
- constexpr int &h(); // expected-note {{here}}
- int &i = h();
+ constexpr int &h(); // expected-note 2{{here}}
+ int &i = h(); // expected-note {{here}} expected-note {{undefined function 'h' cannot be used in a constant expression}}
constexpr int &j() { return b; }
int &k = j();
@@ -202,7 +202,7 @@ namespace References {
int F : f - 11;
int G : g; // expected-error {{constant expression}}
int H : h(); // expected-error {{constant expression}} expected-note {{undefined function 'h'}}
- int I : i; // expected-error {{constant expression}}
+ int I : i; // expected-error {{constant expression}} expected-note {{initializer of 'i' is not a constant expression}}
int J : j();
int K : k;
};
diff --git a/test/PCH/cxx-constexpr.cpp b/test/PCH/cxx-constexpr.cpp
new file mode 100644
index 0000000000..8fe48f7377
--- /dev/null
+++ b/test/PCH/cxx-constexpr.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -pedantic-errors -std=c++98 -emit-pch %s -o %t
+// RUN: %clang_cc1 -pedantic-errors -std=c++98 -include-pch %t -verify %s
+
+// RUN: %clang_cc1 -pedantic-errors -std=c++11 -emit-pch %s -o %t-cxx11
+// RUN: %clang_cc1 -pedantic-errors -std=c++11 -include-pch %t-cxx11 -verify %s
+
+#ifndef HEADER_INCLUDED
+
+#define HEADER_INCLUDED
+extern const int a;
+const int b = a;
+
+#else
+
+const int a = 5;
+typedef int T[b]; // expected-error {{variable length array}} expected-error {{must be an integer constant expression}}
+typedef int T[5];
+
+#endif
diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp
index 5b053e4ce6..94da73fcf6 100644
--- a/test/SemaCXX/constant-expression-cxx11.cpp
+++ b/test/SemaCXX/constant-expression-cxx11.cpp
@@ -8,9 +8,7 @@ static_assert(false, "test"); // expected-error {{test}}
}
-// FIXME: support const T& parameters here.
-//template<typename T> constexpr T id(const T &t) { return t; }
-template<typename T> constexpr T id(T t) { return t; } // expected-note {{here}}
+template<typename T> constexpr T id(const T &t) { return t; } // expected-note {{here}}
// FIXME: support templates here.
//template<typename T> constexpr T min(const T &a, const T &b) {
// return a < b ? a : b;
@@ -104,8 +102,14 @@ namespace CaseStatements {
}
extern int &Recurse1;
-int &Recurse2 = Recurse1, &Recurse1 = Recurse2;
-constexpr int &Recurse3 = Recurse2; // expected-error {{must be initialized by a constant expression}}
+int &Recurse2 = Recurse1; // expected-note 2{{declared here}} expected-note {{initializer of 'Recurse1' is not a constant expression}}
+int &Recurse1 = Recurse2; // expected-note {{declared here}} expected-note {{initializer of 'Recurse2' is not a constant expression}}
+constexpr int &Recurse3 = Recurse2; // expected-error {{must be initialized by a constant expression}} expected-note {{initializer of 'Recurse2' is not a constant expression}}
+</