[cfe-commits] r50021 - in /cfe/trunk: include/clang/AST/Decl.h include/clang/Parse/Scope.h lib/AST/Decl.cpp lib/Sema/Sema.h lib/Sema/SemaDecl.cpp test/Sema/redefinition.c

Douglas Gregor doug.gregor at gmail.com
Sun Apr 20 19:02:59 PDT 2008


Author: dgregor
Date: Sun Apr 20 21:02:58 2008
New Revision: 50021

URL: http://llvm.org/viewvc/llvm-project?rev=50021&view=rev
Log:
Clean up handling of function redeclarations

Added:
    cfe/trunk/test/Sema/redefinition.c   (with props)
Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/Parse/Scope.h
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDecl.cpp

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

==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Sun Apr 20 21:02:58 2008
@@ -288,8 +288,17 @@
   friend Decl* Decl::Create(llvm::Deserializer& D, ASTContext& C);
 };
 
-/// FunctionDecl - An instance of this class is created to represent a function
-/// declaration or definition.
+/// FunctionDecl - An instance of this class is created to represent a
+/// function declaration or definition. 
+///
+/// Since a given function can be declared several times in a program,
+/// there may be several FunctionDecls that correspond to that
+/// function. Only one of those FunctionDecls will be found when
+/// traversing the list of declarations in the context of the
+/// FunctionDecl (e.g., the translation unit); this FunctionDecl
+/// contains all of the information known about the function. Other,
+/// previous declarations of the function are available via the
+/// getPreviousDeclaration() chain. 
 class FunctionDecl : public ValueDecl, public DeclContext {
 public:
   enum StorageClass {
@@ -313,13 +322,25 @@
   bool IsInline : 1;
   bool IsImplicit : 1;
   
+  /// PreviousDeclaration - A link to the previous declaration of this
+  /// same function, NULL if this is the first declaration. For
+  /// example, in the following code, the PreviousDeclaration can be
+  /// traversed several times to see all three declarations of the
+  /// function "f", the last of which is also a definition.
+  ///
+  ///   int f(int x, int y = 1);
+  ///   int f(int x = 0, int y);
+  ///   int f(int x, int y) { return x + y; }
+  FunctionDecl *PreviousDeclaration;
+
   FunctionDecl(DeclContext *CD, SourceLocation L,
                IdentifierInfo *Id, QualType T,
                StorageClass S, bool isInline, ScopedDecl *PrevDecl)
     : ValueDecl(Function, CD, L, Id, T, PrevDecl), 
       DeclContext(Function),
       ParamInfo(0), Body(0), DeclChain(0), SClass(S), 
-      IsInline(isInline), IsImplicit(0) {}
+      IsInline(isInline), IsImplicit(0), PreviousDeclaration(0) {}
+
   virtual ~FunctionDecl();
 public:
   static FunctionDecl *Create(ASTContext &C, DeclContext *CD, SourceLocation L,
@@ -327,7 +348,23 @@
                               StorageClass S = None, bool isInline = false, 
                               ScopedDecl *PrevDecl = 0);
   
-  Stmt *getBody() const { return Body; }
+  /// getBody - Retrieve the body (definition) of the function. The
+  /// function body might be in any of the (re-)declarations of this
+  /// function. The variant that accepts a FunctionDecl pointer will
+  /// set that function declaration to the actual declaration
+  /// containing the body (if there is one).
+  Stmt *getBody(const FunctionDecl *&Definition) const;
+  Stmt *getBody() const { 
+    const FunctionDecl* Definition;
+    return getBody(Definition);
+  }
+
+  /// isThisDeclarationADefinition - Returns whether this specific
+  /// declaration of the function is also a definition. This does not
+  /// determine whether the function has been defined (e.g., in a
+  /// previous definition); for that information, use getBody.
+  bool isThisDeclarationADefinition() const { return Body != 0; }
+
   void setBody(Stmt *B) { Body = B; }
   
   bool isImplicit() { return IsImplicit; }
@@ -336,6 +373,12 @@
   ScopedDecl *getDeclChain() const { return DeclChain; }
   void setDeclChain(ScopedDecl *D) { DeclChain = D; }
 
+  /// getPreviousDeclaration - Return the previous declaration of this
+  /// function.
+  const FunctionDecl *getPreviousDeclaration() const {
+    return PreviousDeclaration;
+  }
+
   // Iterator access to formal parameters.
   unsigned param_size() const { return getNumParams(); }
   typedef ParmVarDecl **param_iterator;
@@ -367,7 +410,11 @@
   }
   StorageClass getStorageClass() const { return StorageClass(SClass); }
   bool isInline() const { return IsInline; }
-    
+   
+  /// AddRedeclaration - Adds the function declaration FD as a
+  /// redeclaration of this function.
+  void AddRedeclaration(FunctionDecl *FD);
+ 
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return D->getKind() == Function; }
   static bool classof(const FunctionDecl *D) { return true; }

Modified: cfe/trunk/include/clang/Parse/Scope.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Scope.h?rev=50021&r1=50020&r2=50021&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Scope.h (original)
+++ cfe/trunk/include/clang/Parse/Scope.h Sun Apr 20 21:02:58 2008
@@ -113,7 +113,7 @@
   void AddDecl(Action::DeclTy *D) {
     DeclsInScope.insert(D);
   }
-  
+
   /// isDeclScope - Return true if this is the scope that the specified decl is
   /// declared in.
   bool isDeclScope(Action::DeclTy *D) {

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

==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Sun Apr 20 21:02:58 2008
@@ -403,6 +403,18 @@
 FunctionDecl::~FunctionDecl() {
   delete[] ParamInfo;
   delete Body;
+  delete PreviousDeclaration;
+}
+
+Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
+  for (const FunctionDecl *FD = this; FD != 0; FD = FD->PreviousDeclaration) {
+    if (FD->Body) {
+      Definition = FD;
+      return FD->Body;
+    }
+  }
+
+  return 0;
 }
 
 unsigned FunctionDecl::getNumParams() const {
@@ -436,6 +448,71 @@
   return NumRequiredArgs;
 }
 
+/// AddRedeclaration - Specifies that this function declaration has been
+/// redeclared by the function declaration FD. FD must be a
+/// redeclaration of this based on the semantics of the language being
+/// translated ("compatible" function types in C, same signatures in
+/// C++). 
+void FunctionDecl::AddRedeclaration(FunctionDecl *FD) {
+  assert(FD->PreviousDeclaration == 0 && 
+         "Redeclaration already has a previous declaration!");
+
+  // Insert FD into the list of previous declarations of this
+  // function.
+  FD->PreviousDeclaration = this->PreviousDeclaration;
+  this->PreviousDeclaration = FD;
+
+  // Swap the contents of this function declaration and FD. This
+  // effectively transforms the original declaration into the most
+  // recent declaration, so that all references to this declaration
+  // remain valid (and have information from *all* declarations),
+  // while retaining all of the information about previous
+  // declarations as well.
+
+  // Swap parameters, so that the most recent parameter names and
+  // exact types (e.g., enum vs int) show up in the original
+  // declaration.
+  ParmVarDecl **thisParamInfo = this->ParamInfo;
+  this->ParamInfo = FD->ParamInfo;
+  FD->ParamInfo = thisParamInfo;
+  
+  // Swap the function body: all declarations share the same function
+  // body, but we keep track of who actually defined that function
+  // body by keeping the pointer to the body stored in that node.
+  Stmt *thisBody = this->Body;
+  this->Body = FD->Body;
+  FD->Body = thisBody;
+
+  // Swap type information: this is important because in C, later
+  // declarations can provide slightly different types (enum vs. int,
+  // for example).
+  QualType thisType = this->getType();
+  this->setType(FD->getType());
+  FD->setType(thisType);
+
+  // Swap location information: this allows us to produce diagnostics
+  // later on that reference the most recent declaration (which has
+  // the most information!) while retaining the location of previous
+  // declarations (good for "redefinition" diagnostics).
+  SourceLocation thisLocation = this->getLocation();
+  this->setLocation(FD->getLocation());
+  FD->setLocation(thisLocation);
+  
+  // Swap attributes. FD will have the union of the attributes from
+  // all previous declarations.
+  if (DeclAttrs) {
+    Attr *thisAttr = (*DeclAttrs)[this];
+    (*DeclAttrs)[this] = (*DeclAttrs)[FD];
+    (*DeclAttrs)[FD] = thisAttr;
+  }
+
+  // If any declaration is inline, the function is inline.
+  this->IsInline |= FD->IsInline;
+
+  // FIXME: Is this the right way to handle storage specifiers?
+  if (FD->SClass) this->SClass = FD->SClass;
+}
+
 //===----------------------------------------------------------------------===//
 // RecordDecl Implementation
 //===----------------------------------------------------------------------===//

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

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Sun Apr 20 21:02:58 2008
@@ -268,7 +268,8 @@
   TypedefDecl *ParseTypedefDecl(Scope *S, Declarator &D, QualType T,
                                 ScopedDecl *LastDecl);
   TypedefDecl *MergeTypeDefDecl(TypedefDecl *New, Decl *Old);
-  FunctionDecl *MergeFunctionDecl(FunctionDecl *New, Decl *Old);
+  FunctionDecl *MergeFunctionDecl(FunctionDecl *New, Decl *Old, 
+                                  bool &Redeclaration);
   VarDecl *MergeVarDecl(VarDecl *New, Decl *Old);
   FunctionDecl *MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old);
 

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Sun Apr 20 21:02:58 2008
@@ -256,8 +256,10 @@
 /// declarator D 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.
-///
-FunctionDecl *Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) {
+/// Redeclaration will be set true if thisNew is a redeclaration OldD.
+FunctionDecl *
+Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, bool &Redeclaration) {
+  Redeclaration = false;
   // Verify the old decl was also a function.
   FunctionDecl *Old = dyn_cast<FunctionDecl>(OldD);
   if (!Old) {
@@ -267,28 +269,31 @@
     return New;
   }
   
-  MergeAttributes(New, Old);
-
   QualType OldQType = Context.getCanonicalType(Old->getType());
   QualType NewQType = Context.getCanonicalType(New->getType());
   
   // C++ [dcl.fct]p3:
   //   All declarations for a function shall agree exactly in both the
   //   return type and the parameter-type-list.
-  if (getLangOptions().CPlusPlus && OldQType == NewQType)
+  if (getLangOptions().CPlusPlus && OldQType == NewQType) {
+    MergeAttributes(New, Old);
+    Redeclaration = true;
     return MergeCXXFunctionDecl(New, Old);
+  }
 
   // C: Function types need to be compatible, not identical. This handles
   // duplicate function decls like "void f(int); void f(enum X);" properly.
   if (!getLangOptions().CPlusPlus &&
       Context.functionTypesAreCompatible(OldQType, NewQType)) {
+    MergeAttributes(New, Old);
+    Redeclaration = true;
     return New;
   }
 
   // A function that has already been declared has been redeclared or defined
   // with a different type- show appropriate diagnostic
   diag::kind PrevDiag;
-  if (Old->getBody())
+  if (Old->isThisDeclarationADefinition())
     PrevDiag = diag::err_previous_definition;
   else if (Old->isImplicit())
     PrevDiag = diag::err_previous_implicit_declaration;
@@ -848,8 +853,18 @@
     // Merge the decl with the existing one if appropriate. Since C functions
     // are in a flat namespace, make sure we consider decls in outer scopes.
     if (PrevDecl) {
-      NewFD = MergeFunctionDecl(NewFD, PrevDecl);
+      bool Redeclaration = false;
+      NewFD = MergeFunctionDecl(NewFD, PrevDecl, Redeclaration);
       if (NewFD == 0) return 0;
+      if (Redeclaration) {
+        // Note that the new declaration is a redeclaration of the
+        // older declaration. Then return the older declaration: the
+        // new one is only kept within the set of previous
+        // declarations for this function.
+        FunctionDecl *OldFD = (FunctionDecl *)PrevDecl;
+        OldFD->AddRedeclaration(NewFD);
+        return OldFD;
+      }
     }
     New = NewFD;
 
@@ -1177,10 +1192,11 @@
   Decl *PrevDcl = LookupDecl(D.getIdentifier(), Decl::IDNS_Ordinary,
                              GlobalScope);
   if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(PrevDcl)) {
-    if (FD->getBody()) {
+    const FunctionDecl *Definition;
+    if (FD->getBody(Definition)) {
       Diag(D.getIdentifierLoc(), diag::err_redefinition, 
            D.getIdentifier()->getName());
-      Diag(FD->getLocation(), diag::err_previous_definition);
+      Diag(Definition->getLocation(), diag::err_previous_definition);
     }
   }
   Decl *decl = static_cast<Decl*>(ActOnDeclarator(GlobalScope, D, 0));

Added: cfe/trunk/test/Sema/redefinition.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/redefinition.c?rev=50021&view=auto

==============================================================================
--- cfe/trunk/test/Sema/redefinition.c (added)
+++ cfe/trunk/test/Sema/redefinition.c Sun Apr 20 21:02:58 2008
@@ -0,0 +1,5 @@
+// RUN: clang %s -fsyntax-only -verify
+int f(int) { } // expected-error{{previous definition is here}}
+int f(int);
+int f(int) { } // expected-error{{redefinition of 'f'}}
+

Propchange: cfe/trunk/test/Sema/redefinition.c

------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/Sema/redefinition.c

------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/Sema/redefinition.c

------------------------------------------------------------------------------
    svn:mime-type = text/plain





More information about the cfe-commits mailing list