diff options
Diffstat (limited to 'clang')
| -rw-r--r-- | clang/include/clang/AST/Decl.h | 57 | ||||
| -rw-r--r-- | clang/include/clang/Parse/Scope.h | 2 | ||||
| -rw-r--r-- | clang/lib/AST/Decl.cpp | 77 | ||||
| -rw-r--r-- | clang/lib/Sema/Sema.h | 3 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 34 | ||||
| -rw-r--r-- | clang/test/Sema/redefinition.c | 5 | 
6 files changed, 162 insertions, 16 deletions
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 0a037998a31..ffa0d4bc9ff 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -288,8 +288,17 @@ protected:    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 @@ private:    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 @@ public:                                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 @@ public:    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 @@ public:    }    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; } diff --git a/clang/include/clang/Parse/Scope.h b/clang/include/clang/Parse/Scope.h index 92d4cd44dc5..5db9b2dd76c 100644 --- a/clang/include/clang/Parse/Scope.h +++ b/clang/include/clang/Parse/Scope.h @@ -113,7 +113,7 @@ public:    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) { diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 6ebaac569ff..11e6bcfb0f4 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -403,6 +403,18 @@ const char *NamedDecl::getName() const {  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 @@ unsigned FunctionDecl::getMinRequiredArguments() const {    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  //===----------------------------------------------------------------------===// diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 6d6a26d95d3..e70da932f61 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -268,7 +268,8 @@ private:    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); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index eec8ce5d0a0..415908a30bb 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -256,8 +256,10 @@ static void MergeAttributes(Decl *New, Decl *Old) {  /// 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 @@ FunctionDecl *Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) {      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 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) {      // 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 @@ Sema::DeclTy *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Declarator &D) {    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)); diff --git a/clang/test/Sema/redefinition.c b/clang/test/Sema/redefinition.c new file mode 100644 index 00000000000..c45779fdeef --- /dev/null +++ b/clang/test/Sema/redefinition.c @@ -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'}} +  | 

