diff options
author | Steve Naroff <snaroff@apple.com> | 2008-01-25 00:51:06 +0000 |
---|---|---|
committer | Steve Naroff <snaroff@apple.com> | 2008-01-25 00:51:06 +0000 |
commit | a99603333fffb57cf9ac37eabb190c8f5afc914b (patch) | |
tree | 23c70393831abe2d1753d69d07599873a06a7848 | |
parent | 0cbc215ccd01ba7ce8e46cac5be147746cf0af4e (diff) |
Replace Sema::CheckVariableInitList, CheckConstantInitList, and CheckForCharArrayInitializer with CheckInitializerListTypes. The new routine now supports struct/union initializers.
This is a modified version of a patch by Eli Friedman. This version passes all of the clang tests and supports vectors:-)
Note that Eli and I are still unsatisfied the implementation of CheckInitializerListTypes(). We'd like to find a more elegant implementation. Nevertheless, this is a complex part of the standard (and may be hard to simplify). Since the complexity is localized to this routine, we can iterate without too much disruption:-)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@46339 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | Sema/Sema.h | 12 | ||||
-rw-r--r-- | Sema/SemaDecl.cpp | 382 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticKinds.def | 4 | ||||
-rw-r--r-- | test/Sema/array-init.c | 45 |
4 files changed, 238 insertions, 205 deletions
diff --git a/Sema/Sema.h b/Sema/Sema.h index 32a587c51a..c402baef94 100644 --- a/Sema/Sema.h +++ b/Sema/Sema.h @@ -740,16 +740,8 @@ private: bool CheckSingleInitializer(Expr *&simpleInit, QualType declType); bool CheckInitExpr(Expr *expr, InitListExpr *IList, unsigned slot, QualType ElementType); - - void CheckVariableInitList(QualType DeclType, InitListExpr *IList, - QualType ElementType, - int &nInitializers, bool &hadError); - void CheckConstantInitList(QualType DeclType, InitListExpr *IList, - QualType ElementType, - int &nInitializers, bool &hadError); - bool CheckForCharArrayInitializer(InitListExpr *IList, QualType ElementType, - int &nInitializers, bool isConstant, - bool &hadError); + bool CheckInitializerListTypes(InitListExpr*& IList, QualType &DeclType, + bool topLevel, unsigned& startIndex); bool CheckForConstantInitializer(Expr *e, QualType t); StringLiteral *IsStringLiteralInit(Expr *Init, QualType DeclType); diff --git a/Sema/SemaDecl.cpp b/Sema/SemaDecl.cpp index a264eeab04..b38cd4cdb6 100644 --- a/Sema/SemaDecl.cpp +++ b/Sema/SemaDecl.cpp @@ -376,140 +376,6 @@ bool Sema::CheckInitExpr(Expr *expr, InitListExpr *IList, unsigned slot, return false; } -void Sema::CheckVariableInitList(QualType DeclType, InitListExpr *IList, - QualType ElementType, - int &nInitializers, bool &hadError) { - unsigned numInits = IList->getNumInits(); - - if (numInits) { - if (CheckForCharArrayInitializer(IList, ElementType, nInitializers, - false, hadError)) - return; - - for (unsigned i = 0; i < numInits; i++) { - Expr *expr = IList->getInit(i); - - if (InitListExpr *InitList = dyn_cast<InitListExpr>(expr)) { - if (const ConstantArrayType *CAT = DeclType->getAsConstantArrayType()) { - int maxElements = CAT->getMaximumElements(); - CheckConstantInitList(DeclType, InitList, ElementType, - maxElements, hadError); - } - } else { - hadError = CheckInitExpr(expr, IList, i, ElementType); - } - nInitializers++; - } - } else { - Diag(IList->getLocStart(), - diag::err_at_least_one_initializer_needed_to_size_array); - hadError = true; - } -} - -bool Sema::CheckForCharArrayInitializer(InitListExpr *IList, - QualType ElementType, - int &nInitializers, bool isConstant, - bool &hadError) -{ - if (ElementType->isPointerType()) - return false; - - if (StringLiteral *literal = dyn_cast<StringLiteral>(IList->getInit(0))) { - // FIXME: Handle wide strings - if (ElementType->isCharType()) { - if (isConstant) { - if (literal->getByteLength() > (unsigned)nInitializers) { - Diag(literal->getSourceRange().getBegin(), - diag::warn_initializer_string_for_char_array_too_long, - literal->getSourceRange()); - } - } else { - nInitializers = literal->getByteLength() + 1; - } - } else { - // FIXME: It might be better if we could point to the declaration - // here, instead of the string literal. - Diag(literal->getSourceRange().getBegin(), - diag::array_of_wrong_type_initialized_from_string, - ElementType.getAsString()); - hadError = true; - } - - // Check for excess initializers - for (unsigned i = 1; i < IList->getNumInits(); i++) { - Expr *expr = IList->getInit(i); - Diag(expr->getLocStart(), - diag::err_excess_initializers_in_char_array_initializer, - expr->getSourceRange()); - } - - return true; - } - - return false; -} - -// FIXME: Doesn't deal with arrays of structures yet. -void Sema::CheckConstantInitList(QualType DeclType, InitListExpr *IList, - QualType ElementType, - int &totalInits, bool &hadError) { - int maxElementsAtThisLevel = 0; - int nInitsAtLevel = 0; - - if (ElementType->isRecordType()) // FIXME: until we support structures... - return; - - if (const ConstantArrayType *CAT = DeclType->getAsConstantArrayType()) { - // We have a constant array type, compute maxElements *at this level*. - maxElementsAtThisLevel = CAT->getMaximumElements(); - // Set DeclType, used below to recurse (for multi-dimensional arrays). - DeclType = CAT->getElementType(); - } else if (DeclType->isScalarType()) { - if (const VectorType *VT = DeclType->getAsVectorType()) - maxElementsAtThisLevel = VT->getNumElements(); - else { - Diag(IList->getLocStart(), diag::warn_braces_around_scalar_init, - IList->getSourceRange()); - maxElementsAtThisLevel = 1; - } - } - // The empty init list "{ }" is treated specially below. - unsigned numInits = IList->getNumInits(); - if (numInits) { - if (CheckForCharArrayInitializer(IList, ElementType, - maxElementsAtThisLevel, - true, hadError)) - return; - - for (unsigned i = 0; i < numInits; i++) { - Expr *expr = IList->getInit(i); - - if (InitListExpr *InitList = dyn_cast<InitListExpr>(expr)) { - CheckConstantInitList(DeclType, InitList, ElementType, - totalInits, hadError); - } else { - hadError = CheckInitExpr(expr, IList, i, ElementType); - nInitsAtLevel++; // increment the number of initializers at this level. - totalInits--; // decrement the total number of initializers. - - // Check if we have space for another initializer. - if (((nInitsAtLevel > maxElementsAtThisLevel) || (totalInits < 0))) - Diag(expr->getLocStart(), diag::warn_excess_initializers, - expr->getSourceRange()); - } - } - if (nInitsAtLevel < maxElementsAtThisLevel) // fill the remaining elements. - totalInits -= (maxElementsAtThisLevel - nInitsAtLevel); - } else { - // we have an initializer list with no elements. - totalInits -= maxElementsAtThisLevel; - if (totalInits < 0) - Diag(IList->getLocStart(), diag::warn_excess_initializers, - IList->getSourceRange()); - } -} - bool Sema::CheckStringLiteralInit(StringLiteral *strLiteral, QualType &DeclT) { if (const VariableArrayType *VAT = DeclT->getAsIncompleteArrayType()) { // C99 6.7.8p14. We have an array of character type with unknown size @@ -535,16 +401,199 @@ bool Sema::CheckStringLiteralInit(StringLiteral *strLiteral, QualType &DeclT) { } StringLiteral *Sema::IsStringLiteralInit(Expr *Init, QualType DeclType) { - StringLiteral *strLiteral = dyn_cast<StringLiteral>(Init); const ArrayType *AT = DeclType->getAsArrayType(); - if (strLiteral && (AT && AT->getElementType()->isCharType())) - return strLiteral; + if (AT && AT->getElementType()->isCharType()) { + return dyn_cast<StringLiteral>(Init); + } return 0; } -bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType) { +// CheckInitializerListTypes - Checks the types of elements of an initializer +// list. This function is recursive: it calls itself to initialize subelements +// of aggregate types. Note that the topLevel parameter essentially refers to +// whether this expression "owns" the initializer list passed in, or if this +// initialization is taking elements out of a parent initializer. Each +// call to this function adds zero or more to startIndex, reports any errors, +// and returns true if it found any inconsistent types. +bool Sema::CheckInitializerListTypes(InitListExpr*& IList, QualType &DeclType, + bool topLevel, unsigned& startIndex) { bool hadError = false; - + + if (DeclType->isScalarType()) { + // The simplest case: initializing a single scalar + if (topLevel) { + Diag(IList->getLocStart(), diag::warn_braces_around_scalar_init, + IList->getSourceRange()); + } + if (startIndex < IList->getNumInits()) { + Expr* expr = IList->getInit(startIndex); + if (InitListExpr *SubInitList = dyn_cast<InitListExpr>(expr)) { + // FIXME: Should an error be reported here instead? + unsigned newIndex = 0; + CheckInitializerListTypes(SubInitList, DeclType, true, newIndex); + } else { + hadError |= CheckInitExpr(expr, IList, startIndex, DeclType); + } + ++startIndex; + } + // FIXME: Should an error be reported for empty initializer list + scalar? + } else if (DeclType->isVectorType()) { + if (startIndex < IList->getNumInits()) { + const VectorType *VT = DeclType->getAsVectorType(); + int maxElements = VT->getNumElements(); + QualType elementType = VT->getElementType(); + + for (int i = 0; i < maxElements; ++i) { + // Don't attempt to go past the end of the init list + if (startIndex >= IList->getNumInits()) + break; + Expr* expr = IList->getInit(startIndex); + if (InitListExpr *SubInitList = dyn_cast<InitListExpr>(expr)) { + unsigned newIndex = 0; + hadError |= CheckInitializerListTypes(SubInitList, elementType, + true, newIndex); + ++startIndex; + } else { + hadError |= CheckInitializerListTypes(IList, elementType, + false, startIndex); + } + } + } + } else if (DeclType->isAggregateType() || DeclType->isUnionType()) { + if (DeclType->isStructureType() || DeclType->isUnionType()) { + if (startIndex < IList->getNumInits() && + Context.typesAreCompatible(IList->getInit(startIndex)->getType(), DeclType)) { + // We found a compatible struct; per the standard, this initializes the + // struct. (The C standard technically says that this only applies for + // initializers for declarations with automatic scope; however, this + // construct is unambiguous anyway because a struct cannot contain + // a type compatible with itself. We'll output an error when we check + // if the initializer is constant.) + // FIXME: Is a call to CheckSingleInitializer required here? + ++startIndex; + } else { + RecordDecl* structDecl = DeclType->getAsRecordType()->getDecl(); + // If structDecl is a forward declaration, this loop won't do anything; + // That's okay, because an error should get printed out elsewhere. It + // might be worthwhile to skip over the rest of the initializer, though. + int numMembers = structDecl->getNumMembers() - + structDecl->hasFlexibleArrayMember(); + for (int i = 0; i < numMembers; i++) { + // Don't attempt to go past the end of the init list + if (startIndex >= IList->getNumInits()) + break; + FieldDecl * curField = structDecl->getMember(i); + if (!curField->getIdentifier()) { + // Don't initialize unnamed fields, e.g. "int : 20;" + continue; + } + QualType fieldType = curField->getType(); + Expr* expr = IList->getInit(startIndex); + if (InitListExpr *SubInitList = dyn_cast<InitListExpr>(expr)) { + unsigned newStart = 0; + hadError |= CheckInitializerListTypes(SubInitList, fieldType, + true, newStart); + ++startIndex; + } else { + hadError |= CheckInitializerListTypes(IList, fieldType, + false, startIndex); + } + if (DeclType->isUnionType()) + break; + } + // FIXME: Implement flexible array initialization GCC extension (it's a + // really messy extension to implement, unfortunately...the necessary + // information isn't actually even here!) + } + } else if (DeclType->isArrayType()) { + // Check for the special-case of initializing an array with a string. + if (startIndex < IList->getNumInits()) { + if (StringLiteral *lit = IsStringLiteralInit(IList->getInit(startIndex), + DeclType)) { + CheckStringLiteralInit(lit, DeclType); + ++startIndex; + if (topLevel && startIndex < IList->getNumInits()) { + // We have leftover initializers; warn + Diag(IList->getInit(startIndex)->getLocStart(), + diag::err_excess_initializers_in_char_array_initializer, + IList->getInit(startIndex)->getSourceRange()); + } + return false; + } + } + int maxElements; + if (const VariableArrayType *VAT = DeclType->getAsVariableArrayType()) { + // FIXME: use a proper constant + maxElements = 0x7FFFFFFF; + // Check for VLAs; in standard C it would be possible to check this + // earlier, but I don't know where clang accepts VLAs (gcc accepts + // them in all sorts of strange places). + if (const Expr *expr = VAT->getSizeExpr()) { + Diag(expr->getLocStart(), diag::err_variable_object_no_init, + expr->getSourceRange()); + hadError = true; + } + } else { + const ConstantArrayType *CAT = DeclType->getAsConstantArrayType(); + maxElements = static_cast<int>(CAT->getSize().getZExtValue()); + } + QualType elementType = DeclType->getAsArrayType()->getElementType(); + int numElements = 0; + for (int i = 0; i < maxElements; ++i, ++numElements) { + // Don't attempt to go past the end of the init list + if (startIndex >= IList->getNumInits()) + break; + Expr* expr = IList->getInit(startIndex); + if (InitListExpr *SubInitList = dyn_cast<InitListExpr>(expr)) { + unsigned newIndex = 0; + hadError |= CheckInitializerListTypes(SubInitList, elementType, + true, newIndex); + ++startIndex; + } else { + hadError |= CheckInitializerListTypes(IList, elementType, + false, startIndex); + } + } + if (DeclType->getAsVariableArrayType()) { + // If this is an incomplete array type, the actual type needs to + // be calculated here + if (numElements == 0) { + // Sizing an array implicitly to zero is not allowed + // (It could in theory be allowed, but it doesn't really matter.) + Diag(IList->getLocStart(), + diag::err_at_least_one_initializer_needed_to_size_array); + hadError = true; + } else { + llvm::APSInt ConstVal(32); + ConstVal = numElements; + DeclType = Context.getConstantArrayType(elementType, ConstVal, + ArrayType::Normal, 0); + } + } + } else { + assert(0 && "Aggregate that isn't a function or array?!"); + } + } else { + // In C, all types are either scalars or aggregates, but + // additional handling is needed here for C++ (and possibly others?). + assert(0 && "Unsupported initializer type"); + } + + // If this init list is a base list, we set the type; an initializer doesn't + // fundamentally have a type, but this makes the ASTs a bit easier to read + if (topLevel) + IList->setType(DeclType); + + if (topLevel && startIndex < IList->getNumInits()) { + // We have leftover initializers; warn + Diag(IList->getInit(startIndex)->getLocStart(), + diag::warn_excess_initializers, + IList->getInit(startIndex)->getSourceRange()); + } + return hadError; +} + +bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType) { // 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->getAsVariablyModifiedType()) @@ -559,53 +608,8 @@ bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType) { return CheckStringLiteralInit(strLiteral, DeclType); return CheckSingleInitializer(Init, DeclType); } - // We have an InitListExpr, make sure we set the type. - Init->setType(DeclType); - - // 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->getAsIncompleteArrayType()) { - - // We have a VariableArrayType with unknown size. Note that only the first - // array can have unknown size. For example, "int [][]" is illegal. - int numInits = 0; - CheckVariableInitList(VAT->getElementType(), InitList, VAT->getBaseType(), - numInits, hadError); - llvm::APSInt ConstVal(32); - - if (!hadError) - ConstVal = numInits; - - // Return a new array type from the number of initializers (C99 6.7.8p22). - - // Note that if there was an error, we will still set the decl type, - // to an array type with 0 elements. - // This is to avoid "incomplete type foo[]" errors when we've already - // reported the real cause of the error. - DeclType = Context.getConstantArrayType(VAT->getElementType(), ConstVal, - ArrayType::Normal, 0); - return hadError; - } - if (const ConstantArrayType *CAT = DeclType->getAsConstantArrayType()) { - int maxElements = CAT->getMaximumElements(); - CheckConstantInitList(DeclType, InitList, CAT->getBaseType(), - maxElements, hadError); - return hadError; - } - if (const VectorType *VT = DeclType->getAsVectorType()) { - int maxElements = VT->getNumElements(); - CheckConstantInitList(DeclType, InitList, VT->getElementType(), - maxElements, hadError); - return hadError; - } - if (DeclType->isScalarType()) { // C99 6.7.8p11: Allow "int x = { 1, 2 };" - int maxElements = 1; - CheckConstantInitList(DeclType, InitList, DeclType, maxElements, hadError); - return hadError; - } - // FIXME: Handle struct/union types, including those appearing in a - // CompoundLiteralExpr... - return hadError; + unsigned newIndex = 0; + return CheckInitializerListTypes(InitList, DeclType, true, newIndex); } Sema::DeclTy * @@ -784,7 +788,8 @@ void Sema::AddInitializerToDecl(DeclTy *dcl, ExprTy *init) { Diag(VDecl->getLocation(), diag::err_block_extern_cant_init); BVD->setInvalidDecl(); } else if (!BVD->isInvalidDecl()) { - CheckInitializerTypes(Init, DclT); + if (CheckInitializerTypes(Init, DclT)) + BVD->setInvalidDecl(); if (SC == VarDecl::Static) // C99 6.7.8p4. CheckForConstantInitializer(Init, DclT); } @@ -792,7 +797,8 @@ void Sema::AddInitializerToDecl(DeclTy *dcl, ExprTy *init) { if (FVD->getStorageClass() == VarDecl::Extern) Diag(VDecl->getLocation(), diag::warn_extern_init); if (!FVD->isInvalidDecl()) - CheckInitializerTypes(Init, DclT); + if (CheckInitializerTypes(Init, DclT)) + FVD->setInvalidDecl(); // C99 6.7.8p4. All file scoped initializers need to be constant. CheckForConstantInitializer(Init, DclT); diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 0fcd8c93b5..53d4946049 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -628,8 +628,6 @@ DIAG(ext_typecheck_zero_array_size, EXTENSION, "zero size arrays are an extension") DIAG(err_at_least_one_initializer_needed_to_size_array, ERROR, "at least one initializer value required to size array") -DIAG(array_of_wrong_type_initialized_from_string, ERROR, - "array of wrong type '%0' initialized from string constant") DIAG(err_array_size_non_int, ERROR, "size of array has non-integer type '%0'") DIAG(err_init_element_not_constant, ERROR, @@ -640,7 +638,7 @@ 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(warn_excess_initializers, EXTENSION, +DIAG(warn_excess_initializers, WARNING, "excess elements in array initializer") DIAG(err_excess_initializers_in_char_array_initializer, ERROR, "excess elements in char array initializer") diff --git a/test/Sema/array-init.c b/test/Sema/array-init.c index 60bb6afd9f..4b94ed18cd 100644 --- a/test/Sema/array-init.c +++ b/test/Sema/array-init.c @@ -66,7 +66,7 @@ void test() { { 1, 3, 5, 2 }, // expected-warning{{excess elements in array initializer}} { 4, 6 }, { 3, 5, 7 }, - { 4, 6, 8 }, // expected-warning{{excess elements in array initializer}} + { 4, 6, 8 }, }; } @@ -152,14 +152,14 @@ void charArrays() char c[] = { "Hello" }; int l[sizeof(c) == 6 ? 1 : -1]; - int i[] = { "Hello "}; // expected-error{{array of wrong type 'int' initialized from string constant}} + int i[] = { "Hello "}; // expected-warning{{incompatible pointer to integer conversion initializing 'char *', expected 'int'}} char c2[] = { "Hello", "Good bye" }; //expected-error{{excess elements in char array initializer}} - int i2[1] = { "Hello" }; //expected-error{{array of wrong type 'int' initialized from string constant}} + int i2[1] = { "Hello" }; //expected-warning{{incompatible pointer to integer conversion initializing 'char *', expected 'int'}} char c3[5] = { "Hello" }; char c4[4] = { "Hello" }; //expected-warning{{initializer-string for char array is too long}} - int i3[] = {}; //expected-error{{at least one initializer value required to size array}} expected-warning{{use of GNU empty initializer extension}} + int i3[] = {}; //expected-error{{at least one initializer value required to size array}} expected-error{{variable has incomplete type 'int []'}} expected-warning{{use of GNU empty initializer extension}} } void variableArrayInit() { @@ -167,3 +167,40 @@ void variableArrayInit() { char strlit[a] = "foo"; //expected-error{{variable-sized object may not be initialized}} int b[a] = { 1, 2, 4 }; //expected-error{{variable-sized object may not be initialized}} } + +// Pure array tests +float r1[10] = {{7}}; //expected-warning{{braces around scalar initializer}} +float r2[] = {{8}}; //expected-warning{{braces around scalar initializer}} +char r3[][5] = {1,2,3,4,5,6}; +char r3_2[sizeof r3 == 10 ? 1 : -1]; +float r4[1][2] = {1,{2},3,4}; //expected-warning{{braces around scalar initializer}} expected-warning{{excess elements in array initializer}} +char r5[][5] = {"aa", "bbb", "ccccc"}; +char r6[sizeof r5 == 15 ? 1 : -1]; +const char r7[] = "zxcv"; +char r8[5] = "5char"; +char r9[5] = "6chars"; //expected-warning{{initializer-string for char array is too long}} + +int r11[0] = {}; //expected-warning{{zero size arrays are an extension}} expected-warning{{use of GNU empty initializer extension}} + +// Some struct tests +void autoStructTest() { +struct s1 {char a; char b;} t1; +struct s2 {struct s1 c;} t2 = { t1 }; +// The following is a less than great diagnostic (though it's on par with EDG). +struct s1 t3[] = {t1, t1, "abc", 0}; //expected-warning{{incompatible pointer to integer conversion initializing 'char *', expected 'char'}} +int t4[sizeof t3 == 6 ? 1 : -1]; +} +struct s3 {void (*a)(void);} t5 = {autoStructTest}; +// GCC extension; flexible array init. Once this is implemented, the warning should be removed. +// Note that clang objc implementation depends on this extension. +struct {int a; int b[];} t6 = {1, {1, 2, 3}}; //expected-warning{{excess elements in array initializer}} +union {char a; int b;} t7[] = {1, 2, 3}; +int t8[sizeof t7 == (3*sizeof(int)) ? 1 : -1]; + +struct bittest{int : 31, a, :21, :12, b;}; +struct bittest bittestvar = {1, 2, 3, 4}; //expected-warning{{excess elements in array initializer}} + +// Not completely sure what should happen here... +int u1 = {}; //expected-warning{{use of GNU empty initializer extension}} expected-warning{{braces around scalar initializer}} +int u2 = {{3}}; //expected-warning{{braces around scalar initializer}} + |