[cfe-commits] r66601 - in /cfe/trunk: include/clang/AST/Decl.h include/clang/Basic/DiagnosticSemaKinds.def lib/AST/Decl.cpp lib/Sema/Sema.cpp lib/Sema/Sema.h lib/Sema/SemaDecl.cpp test/Sema/enum.c test/Sema/incomplete-decl.c test/Sema/init.c test/Sema/tentative-decls.c

Douglas Gregor dgregor at apple.com
Tue Mar 10 16:43:53 PDT 2009


Author: dgregor
Date: Tue Mar 10 18:43:53 2009
New Revision: 66601

URL: http://llvm.org/viewvc/llvm-project?rev=66601&view=rev
Log:
Add type checking for tentative definitions at the end of the
translation unit.

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


Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/Sema/Sema.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/test/Sema/enum.c
    cfe/trunk/test/Sema/incomplete-decl.c
    cfe/trunk/test/Sema/init.c
    cfe/trunk/test/Sema/tentative-decls.c

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=66601&r1=66600&r2=66601&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Tue Mar 10 18:43:53 2009
@@ -233,6 +233,9 @@
   /// condition, e.g., if (int x = foo()) { ... }.
   bool DeclaredInCondition : 1;
 
+  /// \brief The previous declaration of this variable.
+  VarDecl *PreviousDeclaration;
+
   // Move to DeclGroup when it is implemented.
   SourceLocation TypeSpecStartLoc;
   friend class StmtIteratorBase;
@@ -240,8 +243,9 @@
   VarDecl(Kind DK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id,
           QualType T, StorageClass SC, SourceLocation TSSL = SourceLocation())
     : ValueDecl(DK, DC, L, Id, T), Init(0),
-          ThreadSpecified(false), HasCXXDirectInit(false),
-          DeclaredInCondition(false), TypeSpecStartLoc(TSSL) { 
+      ThreadSpecified(false), HasCXXDirectInit(false),
+      DeclaredInCondition(false), PreviousDeclaration(0), 
+      TypeSpecStartLoc(TSSL) { 
     SClass = SC; 
   }
 public:
@@ -261,6 +265,12 @@
   Expr *getInit() { return (Expr*) Init; }
   void setInit(Expr *I) { Init = (Stmt*) I; }
       
+  /// \brief Retrieve the definition of this variable, which may come
+  /// from a previous declaration. Def will be set to the VarDecl that
+  /// contains the initializer, and the result will be that
+  /// initializer.
+  const Expr *getDefinition(const VarDecl *&Def);
+
   void setThreadSpecified(bool T) { ThreadSpecified = T; }
   bool isThreadSpecified() const {
     return ThreadSpecified;
@@ -290,6 +300,14 @@
     DeclaredInCondition = InCondition; 
   }
 
+  /// getPreviousDeclaration - Return the previous declaration of this
+  /// variable.
+  const VarDecl *getPreviousDeclaration() const { return PreviousDeclaration; }
+
+  void setPreviousDeclaration(VarDecl * PrevDecl) {
+    PreviousDeclaration = PrevDecl;
+  }
+
   /// hasLocalStorage - Returns true if a variable with function scope
   ///  is a non-static local variable.
   bool hasLocalStorage() const {
@@ -337,6 +355,10 @@
     }
     return false;
   }
+
+  /// \brief Determine whether this is a tentative definition of a
+  /// variable in C.
+  bool isTentativeDefinition(ASTContext &Context) const;
   
   /// \brief Determines whether this variable is a variable with
   /// external, C linkage.

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def?rev=66601&r1=66600&r2=66601&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def Tue Mar 10 18:43:53 2009
@@ -891,6 +891,11 @@
      "arithmetic on pointer to void type")
 DIAG(err_typecheck_decl_incomplete_type, ERROR,
      "variable has incomplete type %0")
+DIAG(err_tentative_def_incomplete_type, ERROR,
+     "tentative definition has type %0 that is never completed")
+DIAG(err_tentative_def_incomplete_type_arr, ERROR,
+     "tentative definition has array of type %0 that is never completed")
+
 DIAG(err_realimag_invalid_type, ERROR,
      "invalid type %0 to %1 operator")
 DIAG(err_typecheck_sclass_fscope, ERROR,

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=66601&r1=66600&r2=66601&view=diff

==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Tue Mar 10 18:43:53 2009
@@ -273,6 +273,22 @@
 VarDecl::~VarDecl() {
 }
 
+bool VarDecl::isTentativeDefinition(ASTContext &Context) const {
+  if (!isFileVarDecl() || Context.getLangOptions().CPlusPlus)
+    return false;
+
+  return (!getInit() &&
+          (getStorageClass() == None || getStorageClass() == Static));
+}
+
+const Expr *VarDecl::getDefinition(const VarDecl *&Def) {
+  Def = this;
+  while (Def && !Def->getInit())
+    Def = Def->getPreviousDeclaration();
+
+  return Def? Def->getInit() : 0;
+}
+
 //===----------------------------------------------------------------------===//
 // FunctionDecl Implementation
 //===----------------------------------------------------------------------===//

Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=66601&r1=66600&r2=66601&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Tue Mar 10 18:43:53 2009
@@ -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();
+        }
+      }
+    }
+  }
 }
 
 

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=66601&r1=66600&r2=66601&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Tue Mar 10 18:43:53 2009
@@ -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, 

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=66601&r1=66600&r2=66601&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Tue Mar 10 18:43:53 2009
@@ -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;
 }

Modified: cfe/trunk/test/Sema/enum.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/enum.c?rev=66601&r1=66600&r2=66601&view=diff

==============================================================================
--- cfe/trunk/test/Sema/enum.c (original)
+++ cfe/trunk/test/Sema/enum.c Tue Mar 10 18:43:53 2009
@@ -21,7 +21,9 @@
   return sizeof(enum e) ;
 }
 
-enum gccForwardEnumExtension ve; // expected-warning{{ISO C forbids forward references to 'enum' types}}
+enum gccForwardEnumExtension ve; // expected-warning{{ISO C forbids forward references to 'enum' types}} \
+// expected-error{{tentative definition has type 'enum gccForwardEnumExtension' that is never completed}} \
+// expected-note{{forward declaration of 'enum gccForwardEnumExtension'}}
 
 int test2(int i)
 {

Modified: cfe/trunk/test/Sema/incomplete-decl.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/incomplete-decl.c?rev=66601&r1=66600&r2=66601&view=diff

==============================================================================
--- cfe/trunk/test/Sema/incomplete-decl.c (original)
+++ cfe/trunk/test/Sema/incomplete-decl.c Tue Mar 10 18:43:53 2009
@@ -1,9 +1,9 @@
 // RUN: clang -fsyntax-only -verify %s
 
-struct foo; // expected-note 3 {{forward declaration of 'struct foo'}}
+struct foo; // expected-note 4 {{forward declaration of 'struct foo'}}
 
 void b;  // expected-error {{variable has incomplete type 'void'}}
-struct foo f; // // FIXME: error because 'struct foo' is never defined
+struct foo f; // expected-error{{tentative definition has type 'struct foo' that is never completed}}
 
 static void c; // expected-error {{variable has incomplete type 'void'}}
 static struct foo g;  // expected-error {{variable has incomplete type 'struct foo'}}

Modified: cfe/trunk/test/Sema/init.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/init.c?rev=66601&r1=66600&r2=66601&view=diff

==============================================================================
--- cfe/trunk/test/Sema/init.c (original)
+++ cfe/trunk/test/Sema/init.c Tue Mar 10 18:43:53 2009
@@ -74,7 +74,8 @@
 };
 
 // PR3001
-struct s1 s2 = {
+struct s1 s2 = { // expected-error{{tentative definition has type 'struct s1' that is never completed}} \
+  // expected-note{{forward declaration of 'struct s1'}}
     .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'}}

Modified: cfe/trunk/test/Sema/tentative-decls.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/tentative-decls.c?rev=66601&r1=66600&r2=66601&view=diff

==============================================================================
--- cfe/trunk/test/Sema/tentative-decls.c (original)
+++ cfe/trunk/test/Sema/tentative-decls.c Tue Mar 10 18:43:53 2009
@@ -5,7 +5,10 @@
 static struct a x2; // expected-error{{variable has incomplete type 'struct a'}}
 struct a x3[10]; // expected-error{{array has incomplete element type 'struct a'}}
 struct a {int x;};
-struct b x4; // FIXME: error because 'struct b' is never defined
+static struct a x2_okay;
+struct a x3_okay[10];
+struct b x4; // expected-error{{tentative definition has type 'struct b' that is never completed}} \
+            // expected-note{{forward declaration of 'struct b'}}
 
 const int a [1] = {1};
 extern const int a[];
@@ -23,8 +26,8 @@
 extern int i1; // expected-note {{previous definition is here}}
 static int i1; // expected-error{{static declaration of 'i1' follows non-static declaration}}
 
-static int i2 = 5; // expected-note 2 {{previous definition is here}}
-int i2 = 3; // expected-error{{redefinition of 'i2'}} expected-error{{non-static declaration of 'i2' follows static declaration}}
+static int i2 = 5; // expected-note 1 {{previous definition is here}}
+int i2 = 3; // expected-error{{non-static declaration of 'i2' follows static declaration}}
 
 __private_extern__ int pExtern;
 int pExtern = 0;





More information about the cfe-commits mailing list