]> granicus.if.org Git - clang/commitdiff
Explictly track tentative definitions within Sema, then hand those
authorDouglas Gregor <dgregor@apple.com>
Tue, 21 Apr 2009 17:11:58 +0000 (17:11 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 21 Apr 2009 17:11:58 +0000 (17:11 +0000)
tentative definitions off to the ASTConsumer at the end of the
translation unit.

Eliminate CodeGen's internal tracking of tentative definitions, and
instead hook into ASTConsumer::CompleteTentativeDefinition. Also,
tweak the definition-deferal logic for C++, where there are no
tentative definitions.

Fixes <rdar://problem/6808352>, and will make it much easier for
precompiled headers to cope with tentative definitions in the future.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69681 91177308-0d34-0410-b5e6-96231b3b80d8

12 files changed:
include/clang/AST/ASTConsumer.h
lib/AST/Decl.cpp
lib/CodeGen/CodeGenModule.cpp
lib/CodeGen/CodeGenModule.h
lib/CodeGen/ModuleBuilder.cpp
lib/Sema/Sema.cpp
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
test/PCH/external-defs.h
test/Sema/init.c
test/Sema/tentative-decls.c
tools/clang-cc/Backend.cpp

index fd79bf59ea91188a1625760f689f4b55d845aad8..6dc7e13d8f70c3d610f94bd4f86d8e92edb7c338 100644 (file)
@@ -20,6 +20,7 @@ namespace clang {
   class TagDecl;
   class HandleTagDeclDefinition;
   class SemaConsumer; // layering violation required for safe SemaConsumer
+  class VarDecl;
 
 /// ASTConsumer - This is an abstract interface that should be implemented by
 /// clients that read ASTs.  This abstraction layer allows the client to be
@@ -56,6 +57,17 @@ public:
   /// can be defined in declspecs).
   virtual void HandleTagDeclDefinition(TagDecl *D) {}
   
+  /// \brief Callback invoked at the end of a translation unit to
+  /// notify the consumer that the given tentative definition should
+  /// be completed.
+  ///
+  /// The variable declaration itself will be a tentative
+  /// definition. If it had an incomplete array type, its type will
+  /// have already been changed to an array of size 1. However, the
+  /// declaration remains a tentative definition and has not been
+  /// modified by the introduction of an implicit zero initializer.
+  virtual void CompleteTentativeDefinition(VarDecl *D) {}
+
   /// PrintStats - If desired, print any statistics.
   virtual void PrintStats() {
   }
index 8bda32398fc5971625aeea5216f654249281265b..0326b34960fc281fdd8760223218fb07f8834ab8 100644 (file)
@@ -291,7 +291,8 @@ bool VarDecl::isTentativeDefinition(ASTContext &Context) const {
   if (!isFileVarDecl() || Context.getLangOptions().CPlusPlus)
     return false;
 
-  return (!getInit() &&
+  const VarDecl *Def = 0;
+  return (!getDefinition(Def) &&
           (getStorageClass() == None || getStorageClass() == Static));
 }
 
index 0526e7845ccf93be1721b9dadea03dc052ab8e83..9ae93599df1691e867b6efcbec2697727ca96dcd 100644 (file)
@@ -424,13 +424,6 @@ void CodeGenModule::EmitDeferred() {
     // Otherwise, emit the definition and move on to the next one.
     EmitGlobalDefinition(D);
   }
-
-  // Emit any tentative definitions, in reverse order so the most
-  // important (merged) decl will be seen and emitted first.
-  for (std::vector<const VarDecl*>::reverse_iterator 
-         it = TentativeDefinitions.rbegin(), ie = TentativeDefinitions.rend(); 
-       it != ie; ++it)
-    EmitTentativeDefinition(*it);
 }
 
 /// EmitAnnotateAttr - Generate the llvm::ConstantStruct which contains the 
@@ -502,6 +495,7 @@ bool CodeGenModule::MayDeferGeneration(const ValueDecl *Global) {
   
   const VarDecl *VD = cast<VarDecl>(Global);
   assert(VD->isFileVarDecl() && "Invalid decl");
+
   return VD->getStorageClass() == VarDecl::Static;
 }
 
@@ -520,16 +514,14 @@ void CodeGenModule::EmitGlobal(const ValueDecl *Global) {
     const VarDecl *VD = cast<VarDecl>(Global);
     assert(VD->isFileVarDecl() && "Cannot emit local var decl as global.");
 
-    // If this isn't a definition, defer code generation.
-    if (!VD->getInit()) {
-      // If this is a tentative definition, remember it so that we can
-      // emit the common definition if needed. It is important to
-      // defer tentative definitions, since they may have incomplete
-      // type.
-      if (!VD->hasExternalStorage())
-        TentativeDefinitions.push_back(VD);
+    // In C++, if this is marked "extern", defer code generation.
+    if (getLangOptions().CPlusPlus && 
+        VD->getStorageClass() == VarDecl::Extern && !VD->getInit())
+      return;
+
+    // In C, if this isn't a definition, defer code generation.
+    if (!getLangOptions().CPlusPlus && !VD->getInit())
       return;
-    }
   }
 
   // Defer code generation when possible if this is a static definition, inline
@@ -727,6 +719,9 @@ void CodeGenModule::EmitTentativeDefinition(const VarDecl *D) {
   // See if we have already defined this (as a variable), if so we do
   // not need to do anything.
   llvm::GlobalValue *GV = GlobalDeclMap[getMangledName(D)];
+  if (!GV && MayDeferGeneration(D)) // this variable was never referenced
+    return;
+
   if (llvm::GlobalVariable *Var = dyn_cast_or_null<llvm::GlobalVariable>(GV))
     if (Var->hasInitializer())
       return;
index e7924fade534e968c0a11f66987185c2abbce0b7..21ef8da863c7cc47b9542db2599213b8c118a6ee 100644 (file)
@@ -119,14 +119,6 @@ class CodeGenModule : public BlockModule {
   /// is done.
   std::vector<const ValueDecl*> DeferredDeclsToEmit;
 
-  /// TentativeDefinitions - A list of declarations which are
-  /// tentative definitions. Code generation for these must be
-  /// deferred because they are allowed to have incomplete type when
-  /// they are seen. This also allows us to avoid generating an extra
-  /// common definiton in situations where the tentative definition is
-  /// followed by an actual definition.
-  std::vector<const VarDecl*> TentativeDefinitions;
-  
   /// LLVMUsed - List of global values which are required to be
   /// present in the object file; bitcast to i8*. This is used for
   /// forcing visibility of symbols which may otherwise be optimized
@@ -339,6 +331,8 @@ public:
                                     CXXCtorType Type);
   const char *getMangledCXXDtorName(const CXXDestructorDecl *D, 
                                     CXXDtorType Type);
+
+  void EmitTentativeDefinition(const VarDecl *D);
   
   enum GVALinkage {
     GVA_Internal,
@@ -382,7 +376,6 @@ private:
   void EmitGlobalDefinition(const ValueDecl *D);
 
   void EmitGlobalFunctionDefinition(const FunctionDecl *D);
-  void EmitTentativeDefinition(const VarDecl *D);
   void EmitGlobalVarDefinition(const VarDecl *D);
   void EmitAliasDefinition(const ValueDecl *D);
   void EmitObjCPropertyImplementations(const ObjCImplementationDecl *D);
index 896464ed5a8ea5f28a2280e16f0a4b16c6bca6e7..9b85df61da0504cfe61e64df54d352a1b9fa178d 100644 (file)
@@ -83,6 +83,13 @@ namespace {
       if (Builder)
         Builder->Release();
     };
+
+    virtual void CompleteTentativeDefinition(VarDecl *D) {
+      if (Diags.hasErrorOccurred())
+        return;
+
+      Builder->EmitTentativeDefinition(D);
+    }
   };
 }
 
index ed5c431da82f94f0fa063282664a3341a33b12f6..19155b6cc3459d9145015484d4eeed50e764e85c 100644 (file)
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Sema.h"
+#include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/Expr.h"
@@ -232,41 +233,39 @@ void Sema::ActOnEndOfTranslationUnit() {
   //   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 = (*I).getAs<Decl>();
-      if (D->isInvalidDecl())
-        continue;
+  for (llvm::DenseMap<DeclarationName, VarDecl *>::iterator 
+         D = TentativeDefinitions.begin(),
+         DEnd = TentativeDefinitions.end();
+       D != DEnd; ++D) {
+    VarDecl *VD = D->second;
 
-      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).
-              Diag(VD->getLocation(),  diag::warn_tentative_incomplete_array);
-              llvm::APInt 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();
-        }
+    if (VD->isInvalidDecl() || !VD->isTentativeDefinition(Context))
+      continue;
+
+    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).
+        Diag(VD->getLocation(),  diag::warn_tentative_incomplete_array);
+        llvm::APInt 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();
+
+    // Notify the consumer that we've completed a tentative definition.
+    if (!VD->isInvalidDecl())
+      Consumer.CompleteTentativeDefinition(VD);
+
   }
 }
 
index 1f6cd0440c4c94c7322b5bda34fc4b6168123903..f3c337c148edff300f25b69ace795392651a5830 100644 (file)
@@ -233,6 +233,14 @@ public:
   ///     not visible.
   llvm::DenseMap<DeclarationName, NamedDecl *> LocallyScopedExternalDecls;
 
+  /// \brief The set of tentative declarations seen so far in this
+  /// translation unit for which no definition has been seen.
+  ///
+  /// The tentative declarations are indexed by the name of the
+  /// declaration, and only the most recent tentative declaration for
+  /// a given variable will be recorded here.
+  llvm::DenseMap<DeclarationName, VarDecl *> TentativeDefinitions;
+
   IdentifierResolver IdResolver;
 
   // Enum values used by KnownFunctionIDs (see below).
index 716219c034a8139607e838cc1f7c6405bada2935..abde26ae533b820f593d8115ebe239d124510681 100644 (file)
@@ -2545,6 +2545,18 @@ void Sema::AddInitializerToDecl(DeclPtrTy dcl, ExprArg init, bool DirectInit) {
     
   // Attach the initializer to the decl.
   VDecl->setInit(Init);
+
+  // If the previous declaration of VDecl was a tentative definition,
+  // remove it from the set of tentative definitions.
+  if (VDecl->getPreviousDeclaration() &&
+      VDecl->getPreviousDeclaration()->isTentativeDefinition(Context)) {
+    llvm::DenseMap<DeclarationName, VarDecl *>::iterator Pos 
+      = TentativeDefinitions.find(VDecl->getDeclName());
+    assert(Pos != TentativeDefinitions.end() && 
+           "Unrecorded tentative definition?");
+    TentativeDefinitions.erase(Pos);
+  }
+
   return;
 }
 
@@ -2557,6 +2569,11 @@ void Sema::ActOnUninitializedDecl(DeclPtrTy dcl) {
 
   if (VarDecl *Var = dyn_cast<VarDecl>(RealDecl)) {
     QualType Type = Var->getType();
+
+    // Record tentative definitions.
+    if (Var->isTentativeDefinition(Context))
+      TentativeDefinitions[Var->getDeclName()] = Var;
+
     // C++ [dcl.init.ref]p3:
     //   The initializer can be omitted for a reference only in a
     //   parameter declaration (8.3.5), in the declaration of a
index 4d233e2a4049abeaaf7827856a70d7343000e998..bb51a9d64b9fe2f494658045437d499eee8ca3d9 100644 (file)
@@ -14,5 +14,4 @@ static int z;
 int incomplete_array[];
 int incomplete_array2[];
 
-// FIXME: CodeGen problems prevents this from working (<rdar://problem/6762287>)
-// struct S s;
+struct S s;
index cbf75e1bb1c65be4dc9f32066fa56ea2dca9a541..7938ec5568dc8c9661ac581dcde23db9b6276f7c 100644 (file)
@@ -74,8 +74,7 @@ int sym_fw1a_scr[] = {
 };
 
 // PR3001
-struct s1 s2 = { // expected-error{{tentative definition has type 'struct s1' that is never completed}} \
-  // expected-note{{forward declaration of 'struct s1'}}
+struct s1 s2 = {
     .a = sizeof(struct s3), // expected-error {{invalid application of 'sizeof'}} \
                             // expected-note{{forward declaration of 'struct s3'}}
     .b = bogus // expected-error {{use of undeclared identifier 'bogus'}}
index 3a830bf4283debabcca1c6640bbf2699d4905173..85b5ed76bd6bb2253101a73c15a06d9024b9e42f 100644 (file)
@@ -57,3 +57,9 @@ void func2(void)
   extern double *p;
 }
 
+// <rdar://problem/6808352>
+static int a0[];
+static int b0;
+
+static int a0[] = { 4 };
+static int b0 = 5;
index 58ecedb7f2c1216a176946e104bcf97807142e98..2d8ca82e2f1c2c7173af89c8dbb1d3c62f73e295 100644 (file)
@@ -158,6 +158,10 @@ namespace {
                                      "LLVM IR generation of declaration");
       Gen->HandleTagDeclDefinition(D);
     }
+
+    virtual void CompleteTentativeDefinition(VarDecl *D) {
+      Gen->CompleteTentativeDefinition(D);
+    }
   };  
 }