From: Steve Naroff Date: Sun, 2 Sep 2007 20:30:18 +0000 (+0000) Subject: More progress on array initializers. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=38374b05791ee93300b9fbe8ceb3957f54184b37;p=clang More progress on array initializers. - Added Expr::isConstantExpr(). - Added type checking for InitListExpr elements. - Added diagnostic for trying to initialize a variable sized object. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@41674 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/AST/Expr.cpp b/AST/Expr.cpp index d664c46081..c257b3b8e6 100644 --- a/AST/Expr.cpp +++ b/AST/Expr.cpp @@ -350,6 +350,120 @@ Expr::isModifiableLvalueResult Expr::isModifiableLvalue() const { return MLV_Valid; } +bool Expr::isConstantExpr(ASTContext &Ctx, SourceLocation *Loc) const { + + switch (getStmtClass()) { + default: + if (Loc) *Loc = getLocStart(); + return false; + case ParenExprClass: + return cast(this)->getSubExpr()->isConstantExpr(Ctx, Loc); + case StringLiteralClass: + case FloatingLiteralClass: + case IntegerLiteralClass: + case CharacterLiteralClass: + case ImaginaryLiteralClass: + case TypesCompatibleExprClass: + break; + case CallExprClass: { + const CallExpr *CE = cast(this); + llvm::APSInt Result(32); + Result.zextOrTrunc(Ctx.getTypeSize(getType(), CE->getLocStart())); + if (CE->isBuiltinClassifyType(Result)) + break; + if (Loc) *Loc = getLocStart(); + return false; + } + case DeclRefExprClass: + if (isa(cast(this)->getDecl())) + break; + if (Loc) *Loc = getLocStart(); + return false; + case UnaryOperatorClass: { + const UnaryOperator *Exp = cast(this); + + // Get the operand value. If this is sizeof/alignof, do not evalute the + // operand. This affects C99 6.6p3. + if (!Exp->isSizeOfAlignOfOp() && + !Exp->getSubExpr()->isConstantExpr(Ctx, Loc)) + return false; + + switch (Exp->getOpcode()) { + // Address, indirect, pre/post inc/dec, etc are not valid constant exprs. + // See C99 6.6p3. + default: + if (Loc) *Loc = Exp->getOperatorLoc(); + return false; + case UnaryOperator::Extension: + return true; // FIXME: this is wrong. + case UnaryOperator::SizeOf: + case UnaryOperator::AlignOf: + // sizeof(vla) is not a constantexpr: C99 6.5.3.4p2. + if (!Exp->getSubExpr()->getType()->isConstantSizeType(Ctx, Loc)) + return false; + break; + case UnaryOperator::LNot: + case UnaryOperator::Plus: + case UnaryOperator::Minus: + case UnaryOperator::Not: + break; + } + break; + } + case SizeOfAlignOfTypeExprClass: { + const SizeOfAlignOfTypeExpr *Exp = cast(this); + // alignof always evaluates to a constant. + if (Exp->isSizeOf() && !Exp->getArgumentType()->isConstantSizeType(Ctx,Loc)) + return false; + break; + } + case BinaryOperatorClass: { + const BinaryOperator *Exp = cast(this); + + // The LHS of a constant expr is always evaluated and needed. + if (!Exp->getLHS()->isConstantExpr(Ctx, Loc)) + return false; + + if (!Exp->getRHS()->isConstantExpr(Ctx, Loc)) + return false; + + break; + } + case ImplicitCastExprClass: + case CastExprClass: { + const Expr *SubExpr; + SourceLocation CastLoc; + if (const CastExpr *C = dyn_cast(this)) { + SubExpr = C->getSubExpr(); + CastLoc = C->getLParenLoc(); + } else { + SubExpr = cast(this)->getSubExpr(); + CastLoc = getLocStart(); + } + if (!SubExpr->isConstantExpr(Ctx, Loc)) { + if (Loc) *Loc = SubExpr->getLocStart(); + return false; + } + break; + } + case ConditionalOperatorClass: { + const ConditionalOperator *Exp = cast(this); + + if (!Exp->getCond()->isConstantExpr(Ctx, Loc)) + return false; + + if (!Exp->getLHS()->isConstantExpr(Ctx, Loc)) + return false; + + if (!Exp->getRHS()->isConstantExpr(Ctx, Loc)) + return false; + break; + } + } + + return true; +} + /// isIntegerConstantExpr - this recursive routine will test if an expression is /// an integer constant expression. Note: With the introduction of VLA's in /// C99 the result of the sizeof operator is no longer always a constant diff --git a/Sema/Sema.h b/Sema/Sema.h index 38c11a014a..a741657bf6 100644 --- a/Sema/Sema.h +++ b/Sema/Sema.h @@ -445,7 +445,7 @@ private: QualType CheckInitializer(Expr *simpleInit_or_initList, QualType declType, bool isStatic); bool CheckSingleInitializer(Expr *simpleInit, QualType declType); - bool RequireConstantExprs(InitListExpr *IList); + bool CheckInitList(InitListExpr *IList, QualType DType, bool isStatic); /// ConvertIntegerToTypeWarnOnOverflow - Convert the specified APInt to have /// the specified width and sign. If an overflow occurs, detect it and emit diff --git a/Sema/SemaDecl.cpp b/Sema/SemaDecl.cpp index 5eac645727..c2f03a373c 100644 --- a/Sema/SemaDecl.cpp +++ b/Sema/SemaDecl.cpp @@ -289,19 +289,21 @@ bool Sema::CheckSingleInitializer(Expr *Init, QualType DeclType) { return false; } -bool Sema::RequireConstantExprs(InitListExpr *IList) { +bool Sema::CheckInitList(InitListExpr *IList, QualType DType, bool isStatic) { bool hadError = false; for (unsigned i = 0; i < IList->getNumInits(); i++) { Expr *expr = IList->getInit(i); if (InitListExpr *InitList = dyn_cast(expr)) - RequireConstantExprs(InitList); + CheckInitList(InitList, DType, isStatic); else { SourceLocation loc; - // FIXME: should be isConstantExpr()... - if (!expr->isIntegerConstantExpr(Context, &loc)) { + + if (isStatic && !expr->isConstantExpr(Context, &loc)) { // C99 6.7.8p4. Diag(loc, diag::err_init_element_not_constant, expr->getSourceRange()); hadError = true; + } else if (CheckSingleInitializer(expr, DType)) { + hadError = true; // types didn't match. } } } @@ -316,10 +318,27 @@ QualType Sema::CheckInitializer(Expr *Init, QualType DeclType, bool isStatic) { // We have an InitListExpr, make sure we set the type. Init->setType(DeclType); - if (isStatic) // C99 6.7.8p4. - RequireConstantExprs(InitList); - - // FIXME: Lot of checking still to do... + // C99 6.7.8p3: The type of the entity to be initialized shall be an array + // of unknown size ("[]") or an object type that is not a variable array type. + if (const VariableArrayType *VAT = DeclType->getAsVariableArrayType()) { + Expr *expr = VAT->getSizeExpr(); + if (expr) { + Diag(expr->getLocStart(), diag::err_variable_object_no_init, + expr->getSourceRange()); + return QualType(); + } + } + if (const ArrayType *Ary = DeclType->getAsArrayType()) { + // We have a ConstantArrayType or VariableArrayType with unknown size. + QualType ElmtType = Ary->getElementType(); + + // If we have a multi-dimensional array, navigate to the base type. + while ((Ary = ElmtType->getAsArrayType())) + ElmtType = Ary->getElementType(); + + CheckInitList(InitList, ElmtType, isStatic); + } + // FIXME: Handle struct/union types. return DeclType; } diff --git a/Sema/SemaExpr.cpp b/Sema/SemaExpr.cpp index 713ddb446d..8788c2f9dc 100644 --- a/Sema/SemaExpr.cpp +++ b/Sema/SemaExpr.cpp @@ -649,7 +649,9 @@ ParseInitList(SourceLocation LBraceLoc, ExprTy **initlist, unsigned NumInit, // doing the semantic analysis will likely be located elsewhere (i.e. in // consumers of InitListExpr (e.g. ParseDeclarator, ParseCompoundLiteral). - return new InitListExpr(LBraceLoc, InitList, NumInit, RBraceLoc); + InitListExpr *e = new InitListExpr(LBraceLoc, InitList, NumInit, RBraceLoc); + e->setType(Context.VoidTy); // FIXME: just a place holder for now. + return e; } Action::ExprResult Sema:: diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 8f5247af18..037fecb64c 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -99,6 +99,8 @@ public: llvm::APSInt X(32); return isIntegerConstantExpr(X, Ctx, Loc); } + /// isConstantExpr - Return true if this expression is a valid constant expr. + bool isConstantExpr(ASTContext &Ctx, SourceLocation *Loc) const; static bool classof(const Stmt *T) { return T->getStmtClass() >= firstExprConstant && diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 5de34aa774..e396d95b55 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -548,6 +548,8 @@ DIAG(err_block_extern_cant_init, ERROR, "'extern' variable cannot have an initializer") DIAG(warn_extern_init, WARNING, "'extern' variable has an initializer") +DIAG(err_variable_object_no_init, ERROR, + "variable-sized object may not be initialized") DIAG(err_redefinition_of_label, ERROR, "redefinition of label '%0'") diff --git a/test/Sema/array-init.c b/test/Sema/array-init.c index 3ab2f4e578..209b73463a 100644 --- a/test/Sema/array-init.c +++ b/test/Sema/array-init.c @@ -7,12 +7,14 @@ int ary2[] = { x, y, z }; // expected-error{{initializer element is not constant extern int fileScopeExtern[3] = { 1, 3, 5 }; // expected-warning{{'extern' variable has an initializer}} +static int ary3[] = { 1, "abc", 3 }; // expected-warning{{incompatible types assigning 'char *' to 'int'}} + void func() { int x = 1; //int x2[] = { 1, 3, 5 }; - int x3[x] = { 1, 2 }; // gcc-error {{variable-sized object may not be initialized}} + int x3[x] = { 1, 2 }; // expected-error{{variable-sized object may not be initialized}} int x4 = { 1, 2 }; // gcc-warning {{excess elements in array initializer}} @@ -33,4 +35,6 @@ void func() { struct threeElements *p = 7; // expected-warning{{incompatible types assigning 'int' to 'struct threeElements *'}} extern int blockScopeExtern[3] = { 1, 3, 5 }; // expected-error{{'extern' variable cannot have an initializer}} + + static int x2[3] = { 1.0, "abc" , 5.8 }; // expected-warning{{incompatible types assigning 'char *' to 'int'}} }