diff options
-rw-r--r-- | include/clang/AST/Decl.h | 77 | ||||
-rw-r--r-- | include/clang/AST/Expr.h | 8 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticASTKinds.td | 2 | ||||
-rw-r--r-- | lib/AST/ASTImporter.cpp | 5 | ||||
-rw-r--r-- | lib/AST/Decl.cpp | 89 | ||||
-rw-r--r-- | lib/AST/ExprConstant.cpp | 119 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 40 | ||||
-rw-r--r-- | lib/Serialization/ASTReaderDecl.cpp | 8 | ||||
-rw-r--r-- | lib/Serialization/ASTWriterDecl.cpp | 7 | ||||
-rw-r--r-- | test/CXX/basic/basic.types/p10.cpp | 9 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp | 21 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp | 14 | ||||
-rw-r--r-- | test/CXX/expr/expr.const/p2-0x.cpp | 10 | ||||
-rw-r--r-- | test/PCH/cxx-constexpr.cpp | 19 | ||||
-rw-r--r-- | test/SemaCXX/constant-expression-cxx11.cpp | 67 | ||||
-rw-r--r-- | test/SemaCXX/constant-expression.cpp | 13 | ||||
-rw-r--r-- | test/SemaCXX/constexpr-depth.cpp | 6 | ||||
-rw-r--r-- | test/SemaCXX/constexpr-printing.cpp | 9 | ||||
-rw-r--r-- | test/SemaCXX/cxx0x-class.cpp | 4 | ||||
-rw-r--r-- | test/SemaCXX/i-c-e-cxx.cpp | 4 |
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}} + |