I figured I should get this out into the open, since my holding onto
it isn't very useful.  It's essentially a patch replacing the whole of
initialization-list processing sema with a version written from
scratch.

I'm not really asking for review of this, because it has quite a few weaknesses.

1. It rewrites the whole thing from scratch, which is probably not the
best idea, even if it is only a few hundred lines.
2. I'm not really satisfied with the way this patch is written; it has
a lot of code duplication. Some of the logic is a bit tricky, and both
the duplicate loops both within the method and the inablility to reuse
the logic for codegen/analysis are serious issues.
3. It's not complete: I haven't gone over any of the logic required for vectors.

That said, the code does work, and I believe it implements C99 rules
correctly, so it might be useful source of ideas for Steve or whoever
else touches this code.

-Eli
Index: Sema/Sema.h
===================================================================
--- Sema/Sema.h (revision 46191)
+++ Sema/Sema.h (working copy)
@@ -739,7 +739,11 @@
   bool CheckSingleInitializer(Expr *&simpleInit, QualType declType);
   bool CheckInitExpr(Expr *expr, InitListExpr *IList, unsigned slot,
                      QualType ElementType);
-                     
+
+bool CheckInitializerListTypes(InitListExpr*& IList, QualType &DeclType,
+                                     bool topLevel, unsigned& startIndex);
+bool IsStringLiteralInit(Expr* Expr, QualType& DeclType);
+
   void CheckVariableInitList(QualType DeclType, InitListExpr *IList, 
                              QualType ElementType,
                              int &nInitializers, bool &hadError);
Index: Sema/SemaDecl.cpp
===================================================================
--- Sema/SemaDecl.cpp   (revision 46191)
+++ Sema/SemaDecl.cpp   (working copy)
@@ -354,7 +354,208 @@
   
   return dyn_cast_or_null<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));
 }
+#if 1
+bool Sema::IsStringLiteralInit(Expr* InitExpr, QualType& DeclType) {
+  if (const VariableArrayType *VAT = DeclType->getAsVariableArrayType())
+    if (VAT->getSizeExpr())
+      return false;
+  if (StringLiteral *strLiteral = dyn_cast<StringLiteral>(InitExpr)) {
+    if (const ArrayType* arrType = DeclType->getAsArrayType()) {
+      // Note that we don't want to try and promote literal strings
+      // initializing arrays
+      QualType arrayElementType = arrType->getElementType();
+      const ConstantArrayType* strArrayType = 
strLiteral->getType()->getAsConstantArrayType();
+      QualType strElementType = strArrayType->getElementType();
+      if (Context.typesAreCompatible(strElementType, arrayElementType)) {
+        if (const ConstantArrayType* CAT = arrType->getAsConstantArrayType()) {
+          if (strArrayType->getMaximumElements() > CAT->getMaximumElements() + 
1) {
+            Diag(strLiteral->getSourceRange().getBegin(),
+                 diag::warn_initializer_string_for_char_array_too_long,
+                 strLiteral->getSourceRange());
+          }
+          strLiteral->setType(DeclType);
+        } else {
+          llvm::APSInt ConstVal(32);
+          ConstVal = strArrayType->getMaximumElements();
+          // Return a new array type (C99 6.7.8p22).
+          DeclType = Context.getConstantArrayType(arrType->getElementType(), 
ConstVal,
+                                                  ArrayType::Normal, 0);
+          // set type from "char *" to "constant array of char".
+          strLiteral->setType(DeclType);
+        }
+        return true;
+      }
+    }
+  }
+  return false;
+}
 
+// 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
+    // FIXME: allow initializer lists for vectors
+    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->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 information 
needed
+        // 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() &&
+          IsStringLiteralInit(IList->getInit(startIndex), DeclType)) {
+        ++startIndex;
+        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();
+        // FIXME: Is this really the right way to get this number?
+        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->getLocStart(), diag::warn_excess_initializers, 
+         IList->getSourceRange());
+  }
+  return hadError;
+}
+
+bool Sema::CheckInitializerTypes(Expr *&Init, QualType& DeclType) {
+  InitListExpr *InitList = dyn_cast<InitListExpr>(Init);
+  if (!InitList) {
+    // Check if this is a string literal initializing an array; we check
+    // here because CheckSingleInitializer expects a regular assignment.
+    if (IsStringLiteralInit(Init, DeclType))
+      return false;
+    return CheckSingleInitializer(Init, DeclType);
+  }
+
+  unsigned newIndex = 0;
+  return CheckInitializerListTypes(InitList, DeclType, true, newIndex);
+}
+#endif
 bool Sema::CheckSingleInitializer(Expr *&Init, QualType DeclType) {  
   // Get the type before calling CheckSingleAssignmentConstraints(), since
   // it can promote the expression.
@@ -375,7 +576,7 @@
     IList->setInit(slot, expr);
   return false;
 }
-
+#if 0
 void Sema::CheckVariableInitList(QualType DeclType, InitListExpr *IList, 
                                  QualType ElementType,
                                  int &nInitializers, bool &hadError) {
@@ -596,7 +797,7 @@
   // CompoundLiteralExpr...
   return hadError;
 }
-
+#endif
 Sema::DeclTy *
 Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) {
   ScopedDecl *LastDeclarator = dyn_cast_or_null<ScopedDecl>((Decl *)lastDecl);
_______________________________________________
cfe-dev mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev

Reply via email to