diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index fb1ce33abf93..e0b3c05a662d 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -520,7 +520,10 @@ public: /// isConstantInitializer - Returns true if this expression can be emitted to /// IR as a constant, and thus can be used as a constant initializer in C. - bool isConstantInitializer(ASTContext &Ctx, bool ForRef) const; + /// If this expression is not constant and Culprit is non-null, + /// it is used to store the address of first non constant expr. + bool isConstantInitializer(ASTContext &Ctx, bool ForRef, + const Expr **Culprit = nullptr) const; /// EvalStatus is a struct with detailed info about an evaluation in progress. struct EvalStatus { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index aea2f432ad5e..7e1b2d943602 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2669,7 +2669,8 @@ bool Expr::hasAnyTypeDependentArguments(ArrayRef Exprs) { return false; } -bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const { +bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, + const Expr **Culprit) const { // This function is attempting whether an expression is an initializer // which can be evaluated at compile-time. It very closely parallels // ConstExprEmitter in CGExprConstant.cpp; if they don't match, it @@ -2681,7 +2682,11 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const { if (IsForRef) { EvalResult Result; - return EvaluateAsLValue(Result, Ctx) && !Result.HasSideEffects; + if (EvaluateAsLValue(Result, Ctx) && !Result.HasSideEffects) + return true; + if (Culprit) + *Culprit = this; + return false; } switch (getStmtClass()) { @@ -2700,7 +2705,7 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const { // Trivial copy constructor assert(CE->getNumArgs() == 1 && "trivial ctor with > 1 argument"); - return CE->getArg(0)->isConstantInitializer(Ctx, false); + return CE->getArg(0)->isConstantInitializer(Ctx, false, Culprit); } break; @@ -2710,14 +2715,14 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const { // "struct x {int x;} x = (struct x) {};". // FIXME: This accepts other cases it shouldn't! const Expr *Exp = cast(this)->getInitializer(); - return Exp->isConstantInitializer(Ctx, false); + return Exp->isConstantInitializer(Ctx, false, Culprit); } case InitListExprClass: { const InitListExpr *ILE = cast(this); if (ILE->getType()->isArrayType()) { unsigned numInits = ILE->getNumInits(); for (unsigned i = 0; i < numInits; i++) { - if (!ILE->getInit(i)->isConstantInitializer(Ctx, false)) + if (!ILE->getInit(i)->isConstantInitializer(Ctx, false, Culprit)) return false; } return true; @@ -2741,11 +2746,14 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const { if (Field->isBitField()) { // Bitfields have to evaluate to an integer. llvm::APSInt ResultTmp; - if (!Elt->EvaluateAsInt(ResultTmp, Ctx)) + if (!Elt->EvaluateAsInt(ResultTmp, Ctx)) { + if (Culprit) + *Culprit = Elt; return false; + } } else { bool RefType = Field->getType()->isReferenceType(); - if (!Elt->isConstantInitializer(Ctx, RefType)) + if (!Elt->isConstantInitializer(Ctx, RefType, Culprit)) return false; } } @@ -2759,19 +2767,22 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const { return true; case ParenExprClass: return cast(this)->getSubExpr() - ->isConstantInitializer(Ctx, IsForRef); + ->isConstantInitializer(Ctx, IsForRef, Culprit); case GenericSelectionExprClass: return cast(this)->getResultExpr() - ->isConstantInitializer(Ctx, IsForRef); + ->isConstantInitializer(Ctx, IsForRef, Culprit); case ChooseExprClass: - if (cast(this)->isConditionDependent()) + if (cast(this)->isConditionDependent()) { + if (Culprit) + *Culprit = this; return false; + } return cast(this)->getChosenSubExpr() - ->isConstantInitializer(Ctx, IsForRef); + ->isConstantInitializer(Ctx, IsForRef, Culprit); case UnaryOperatorClass: { const UnaryOperator* Exp = cast(this); if (Exp->getOpcode() == UO_Extension) - return Exp->getSubExpr()->isConstantInitializer(Ctx, false); + return Exp->getSubExpr()->isConstantInitializer(Ctx, false, Culprit); break; } case CXXFunctionalCastExprClass: @@ -2791,25 +2802,29 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const { CE->getCastKind() == CK_ConstructorConversion || CE->getCastKind() == CK_NonAtomicToAtomic || CE->getCastKind() == CK_AtomicToNonAtomic) - return CE->getSubExpr()->isConstantInitializer(Ctx, false); + return CE->getSubExpr()->isConstantInitializer(Ctx, false, Culprit); break; } case MaterializeTemporaryExprClass: return cast(this)->GetTemporaryExpr() - ->isConstantInitializer(Ctx, false); + ->isConstantInitializer(Ctx, false, Culprit); case SubstNonTypeTemplateParmExprClass: return cast(this)->getReplacement() - ->isConstantInitializer(Ctx, false); + ->isConstantInitializer(Ctx, false, Culprit); case CXXDefaultArgExprClass: return cast(this)->getExpr() - ->isConstantInitializer(Ctx, false); + ->isConstantInitializer(Ctx, false, Culprit); case CXXDefaultInitExprClass: return cast(this)->getExpr() - ->isConstantInitializer(Ctx, false); + ->isConstantInitializer(Ctx, false, Culprit); } - return isEvaluatable(Ctx); + if (isEvaluatable(Ctx)) + return true; + if (Culprit) + *Culprit = this; + return false; } bool Expr::HasSideEffects(const ASTContext &Ctx) const { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 88f099f96d29..a3dd2e7462c5 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7931,10 +7931,11 @@ bool Sema::CheckForConstantInitializer(Expr *Init, QualType DclT) { // "may accept other forms of constant expressions" exception. // (We never end up here for C++, so the constant expression // rules there don't matter.) - if (Init->isConstantInitializer(Context, false)) + const Expr *Culprit; + if (Init->isConstantInitializer(Context, false, &Culprit)) return false; - Diag(Init->getExprLoc(), diag::err_init_element_not_constant) - << Init->getSourceRange(); + Diag(Culprit->getExprLoc(), diag::err_init_element_not_constant) + << Culprit->getSourceRange(); return true; } @@ -8415,6 +8416,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // static storage duration shall be constant expressions or string literals. // C++ does not have this restriction. if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl()) { + const Expr *Culprit; if (VDecl->getStorageClass() == SC_Static) CheckForConstantInitializer(Init, DclT); // C89 is stricter than C99 for non-static aggregate types. @@ -8423,10 +8425,10 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // constant expressions. else if (!getLangOpts().C99 && VDecl->getType()->isAggregateType() && isa(Init) && - !Init->isConstantInitializer(Context, false)) - Diag(Init->getExprLoc(), + !Init->isConstantInitializer(Context, false, &Culprit)) + Diag(Culprit->getExprLoc(), diag::ext_aggregate_init_not_constant) - << Init->getSourceRange(); + << Culprit->getSourceRange(); } } else if (VDecl->isStaticDataMember() && VDecl->getLexicalDeclContext()->isRecord()) { @@ -8897,6 +8899,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { } if (var->getTLSKind() == VarDecl::TLS_Static) { + const Expr *Culprit; if (var->getType().isDestructedType()) { // GNU C++98 edits for __thread, [basic.start.term]p3: // The type of an object with thread storage duration shall not @@ -8906,12 +8909,13 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { Diag(var->getLocation(), diag::note_use_thread_local); } else if (getLangOpts().CPlusPlus && var->hasInit() && !var->getInit()->isConstantInitializer( - Context, var->getType()->isReferenceType())) { + Context, var->getType()->isReferenceType(), &Culprit)) { // GNU C++98 edits for __thread, [basic.start.init]p4: // An object of thread storage duration shall not require dynamic // initialization. // FIXME: Need strict checking here. - Diag(var->getLocation(), diag::err_thread_dynamic_init); + Diag(Culprit->getExprLoc(), diag::err_thread_dynamic_init) + << Culprit->getSourceRange(); if (getLangOpts().CPlusPlus11) Diag(var->getLocation(), diag::note_use_thread_local); } diff --git a/clang/test/Sema/array-init.c b/clang/test/Sema/array-init.c index ae2c7425662b..4cc5e412c2d5 100644 --- a/clang/test/Sema/array-init.c +++ b/clang/test/Sema/array-init.c @@ -282,7 +282,12 @@ int a5[] = (int5){1, 2, 3, 4, 5}; // expected-warning{{initialization of an arra int a6[5] = (int[]){1, 2, 3}; // expected-error{{cannot initialize array of type 'int [5]' with array of type 'int [3]'}} int nonconst_value(); -int a7[5] = (int[5]){ 1, 2, 3, 4, nonconst_value() }; // expected-error{{initializer element is not a compile-time constant}} +int a7[5] = (int[5]){ 1, + 2, + 3, + 4, + nonconst_value() // expected-error{{initializer element is not a compile-time constant}} +}; // __attribute__((weak)) const unsigned int test10_bound = 10;