Add type checking for tentative definitions at the end of the
translation unit.

Thread the various declarations of variables via
VarDecl::getPreviousDeclaration.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@66601 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 57ed988..d861013 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -218,7 +218,52 @@
 /// translation unit when EOF is reached and all but the top-level scope is
 /// popped.
 void Sema::ActOnEndOfTranslationUnit() {
+  // C99 6.9.2p2:
+  //   A declaration of an identifier for an object that has file
+  //   scope without an initializer, and without a storage-class
+  //   specifier or with the storage-class specifier static,
+  //   constitutes a tentative definition. If a translation unit
+  //   contains one or more tentative definitions for an identifier,
+  //   and the translation unit contains no external definition for
+  //   that identifier, then the behavior is exactly as if the
+  //   translation unit contains a file scope declaration of that
+  //   identifier, with the composite type as of the end of the
+  //   translation unit, with an initializer equal to 0.
+  if (!getLangOptions().CPlusPlus) {
+    // Note: we traverse the scope's list of declarations rather than
+    // the DeclContext's list, because we only want to see the most
+    // recent declaration of each identifier.
+    for (Scope::decl_iterator I = TUScope->decl_begin(), 
+                           IEnd = TUScope->decl_end();
+         I != IEnd; ++I) {
+      Decl *D = static_cast<Decl *>(*I);
+      if (D->isInvalidDecl())
+        continue;
 
+      if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
+        if (VD->isTentativeDefinition(Context)) {
+          if (const IncompleteArrayType *ArrayT 
+                = Context.getAsIncompleteArrayType(VD->getType())) {
+            if (RequireCompleteType(VD->getLocation(), 
+                                    ArrayT->getElementType(),
+                                 diag::err_tentative_def_incomplete_type_arr))
+              VD->setInvalidDecl();
+            else {
+              // Set the length of the array to 1 (C99 6.9.2p5).
+              llvm::APSInt One(Context.getTypeSize(Context.getSizeType()), 
+                               true);
+              QualType T 
+                = Context.getConstantArrayType(ArrayT->getElementType(),
+                                               One, ArrayType::Normal, 0);
+              VD->setType(T);
+            }
+          } else if (RequireCompleteType(VD->getLocation(), VD->getType(), 
+                                    diag::err_tentative_def_incomplete_type))
+            VD->setInvalidDecl();
+        }
+      }
+    }
+  }
 }
 
 
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index c4964bf..b3669bc 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -451,7 +451,6 @@
   bool MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old);
   bool MergeVarDecl(VarDecl *New, Decl *Old);
   bool MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old);
-  void CheckForFileScopedRedefinitions(Scope *S, VarDecl *VD);
 
   /// C++ Overloading.
   bool IsOverload(FunctionDecl *New, Decl* OldD, 
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index c5768fd..35573bb 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -776,56 +776,6 @@
   return false;
 }
 
-/// Predicate for C "tentative" external object definitions (C99 6.9.2).
-static bool isTentativeDefinition(VarDecl *VD) {
-  if (VD->isFileVarDecl())
-    return (!VD->getInit() &&
-            (VD->getStorageClass() == VarDecl::None ||
-             VD->getStorageClass() == VarDecl::Static));
-  return false;
-}
-
-/// CheckForFileScopedRedefinitions - Make sure we forgo redefinition errors
-/// when dealing with C "tentative" external object definitions (C99 6.9.2).
-void Sema::CheckForFileScopedRedefinitions(Scope *S, VarDecl *VD) {
-  bool VDIsTentative = isTentativeDefinition(VD);
-  bool VDIsIncompleteArray = VD->getType()->isIncompleteArrayType();
-  
-  // FIXME: I don't think this will actually see all of the
-  // redefinitions. Can't we check this property on-the-fly?
-  for (IdentifierResolver::iterator I = IdResolver.begin(VD->getIdentifier()), 
-                                    E = IdResolver.end(); 
-       I != E; ++I) {
-    if (*I != VD && isDeclInScope(*I, VD->getDeclContext(), S)) {
-      VarDecl *OldDecl = dyn_cast<VarDecl>(*I);
-      
-      // Handle the following case:
-      //   int a[10];
-      //   int a[];   - the code below makes sure we set the correct type. 
-      //   int a[11]; - this is an error, size isn't 10.
-      if (OldDecl && VDIsTentative && VDIsIncompleteArray && 
-          OldDecl->getType()->isConstantArrayType())
-        VD->setType(OldDecl->getType());
-      
-      // Check for "tentative" definitions. We can't accomplish this in 
-      // MergeVarDecl since the initializer hasn't been attached.
-      if (!OldDecl || isTentativeDefinition(OldDecl) || VDIsTentative)
-        continue;
-  
-      // Handle __private_extern__ just like extern.
-      if (OldDecl->getStorageClass() != VarDecl::Extern &&
-          OldDecl->getStorageClass() != VarDecl::PrivateExtern &&
-          VD->getStorageClass() != VarDecl::Extern &&
-          VD->getStorageClass() != VarDecl::PrivateExtern) {
-        Diag(VD->getLocation(), diag::err_redefinition) << VD->getDeclName();
-        Diag(OldDecl->getLocation(), diag::note_previous_definition);
-        // One redefinition error is enough.
-        break;
-      }
-    }
-  }
-}
-
 /// MergeVarDecl - We just parsed a variable 'New' which has the same name
 /// and scope as a previous declaration 'Old'.  Figure out how to resolve this
 /// situation, merging decls or emitting diagnostics as appropriate.
@@ -876,6 +826,10 @@
     Diag(Old->getLocation(), diag::note_previous_definition);
     return true;
   }
+
+  // Keep a chain of previous declarations.
+  New->setPreviousDeclaration(Old);
+
   return false;
 }
 
@@ -2168,6 +2122,15 @@
     return;
   }
 
+  const VarDecl *Def = 0;
+  if (VDecl->getDefinition(Def)) {
+    Diag(VDecl->getLocation(), diag::err_redefinition) 
+      << VDecl->getDeclName();
+    Diag(Def->getLocation(), diag::note_previous_definition);
+    VDecl->setInvalidDecl();
+    return;
+  }
+
   // Take ownership of the expression, now that we're sure we have somewhere
   // to put it.
   Expr *Init = static_cast<Expr *>(init.release());
@@ -2349,7 +2312,7 @@
     // storage-class specifier or with the storage-class specifier "static",
     // constitutes a tentative definition. Note: A tentative definition with
     // external linkage is valid (C99 6.2.2p5).
-    if (!getLangOptions().CPlusPlus && isTentativeDefinition(IDecl)) {
+    if (IDecl->isTentativeDefinition(Context)) {
       QualType CheckType = T;
       unsigned DiagID = diag::err_typecheck_decl_incomplete_type;
 
@@ -2369,8 +2332,6 @@
         IDecl->setInvalidDecl();
       }
     }
-    if (IDecl->isFileVarDecl())
-      CheckForFileScopedRedefinitions(S, IDecl);
   }
   return NewGroup;
 }