forked from OSchip/llvm-project
Clean up handling of function redeclarations
llvm-svn: 50021
This commit is contained in:
parent
a5b11705b6
commit
89f238cbe1
|
@ -288,8 +288,17 @@ protected:
|
||||||
friend Decl* Decl::Create(llvm::Deserializer& D, ASTContext& C);
|
friend Decl* Decl::Create(llvm::Deserializer& D, ASTContext& C);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// FunctionDecl - An instance of this class is created to represent a function
|
/// FunctionDecl - An instance of this class is created to represent a
|
||||||
/// declaration or definition.
|
/// 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 {
|
class FunctionDecl : public ValueDecl, public DeclContext {
|
||||||
public:
|
public:
|
||||||
enum StorageClass {
|
enum StorageClass {
|
||||||
|
@ -313,13 +322,25 @@ private:
|
||||||
bool IsInline : 1;
|
bool IsInline : 1;
|
||||||
bool IsImplicit : 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,
|
FunctionDecl(DeclContext *CD, SourceLocation L,
|
||||||
IdentifierInfo *Id, QualType T,
|
IdentifierInfo *Id, QualType T,
|
||||||
StorageClass S, bool isInline, ScopedDecl *PrevDecl)
|
StorageClass S, bool isInline, ScopedDecl *PrevDecl)
|
||||||
: ValueDecl(Function, CD, L, Id, T, PrevDecl),
|
: ValueDecl(Function, CD, L, Id, T, PrevDecl),
|
||||||
DeclContext(Function),
|
DeclContext(Function),
|
||||||
ParamInfo(0), Body(0), DeclChain(0), SClass(S),
|
ParamInfo(0), Body(0), DeclChain(0), SClass(S),
|
||||||
IsInline(isInline), IsImplicit(0) {}
|
IsInline(isInline), IsImplicit(0), PreviousDeclaration(0) {}
|
||||||
|
|
||||||
virtual ~FunctionDecl();
|
virtual ~FunctionDecl();
|
||||||
public:
|
public:
|
||||||
static FunctionDecl *Create(ASTContext &C, DeclContext *CD, SourceLocation L,
|
static FunctionDecl *Create(ASTContext &C, DeclContext *CD, SourceLocation L,
|
||||||
|
@ -327,7 +348,23 @@ public:
|
||||||
StorageClass S = None, bool isInline = false,
|
StorageClass S = None, bool isInline = false,
|
||||||
ScopedDecl *PrevDecl = 0);
|
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; }
|
void setBody(Stmt *B) { Body = B; }
|
||||||
|
|
||||||
bool isImplicit() { return IsImplicit; }
|
bool isImplicit() { return IsImplicit; }
|
||||||
|
@ -336,6 +373,12 @@ public:
|
||||||
ScopedDecl *getDeclChain() const { return DeclChain; }
|
ScopedDecl *getDeclChain() const { return DeclChain; }
|
||||||
void setDeclChain(ScopedDecl *D) { DeclChain = D; }
|
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.
|
// Iterator access to formal parameters.
|
||||||
unsigned param_size() const { return getNumParams(); }
|
unsigned param_size() const { return getNumParams(); }
|
||||||
typedef ParmVarDecl **param_iterator;
|
typedef ParmVarDecl **param_iterator;
|
||||||
|
@ -368,6 +411,10 @@ public:
|
||||||
StorageClass getStorageClass() const { return StorageClass(SClass); }
|
StorageClass getStorageClass() const { return StorageClass(SClass); }
|
||||||
bool isInline() const { return IsInline; }
|
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.
|
// Implement isa/cast/dyncast/etc.
|
||||||
static bool classof(const Decl *D) { return D->getKind() == Function; }
|
static bool classof(const Decl *D) { return D->getKind() == Function; }
|
||||||
static bool classof(const FunctionDecl *D) { return true; }
|
static bool classof(const FunctionDecl *D) { return true; }
|
||||||
|
|
|
@ -403,6 +403,18 @@ const char *NamedDecl::getName() const {
|
||||||
FunctionDecl::~FunctionDecl() {
|
FunctionDecl::~FunctionDecl() {
|
||||||
delete[] ParamInfo;
|
delete[] ParamInfo;
|
||||||
delete Body;
|
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 {
|
unsigned FunctionDecl::getNumParams() const {
|
||||||
|
@ -436,6 +448,71 @@ unsigned FunctionDecl::getMinRequiredArguments() const {
|
||||||
return NumRequiredArgs;
|
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
|
// RecordDecl Implementation
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
|
@ -268,7 +268,8 @@ private:
|
||||||
TypedefDecl *ParseTypedefDecl(Scope *S, Declarator &D, QualType T,
|
TypedefDecl *ParseTypedefDecl(Scope *S, Declarator &D, QualType T,
|
||||||
ScopedDecl *LastDecl);
|
ScopedDecl *LastDecl);
|
||||||
TypedefDecl *MergeTypeDefDecl(TypedefDecl *New, Decl *Old);
|
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);
|
VarDecl *MergeVarDecl(VarDecl *New, Decl *Old);
|
||||||
FunctionDecl *MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old);
|
FunctionDecl *MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old);
|
||||||
|
|
||||||
|
|
|
@ -256,8 +256,10 @@ static void MergeAttributes(Decl *New, Decl *Old) {
|
||||||
/// declarator D which has the same name and scope as a previous
|
/// declarator D which has the same name and scope as a previous
|
||||||
/// declaration 'Old'. Figure out how to resolve this situation,
|
/// declaration 'Old'. Figure out how to resolve this situation,
|
||||||
/// merging decls or emitting diagnostics as appropriate.
|
/// merging decls or emitting diagnostics as appropriate.
|
||||||
///
|
/// Redeclaration will be set true if thisNew is a redeclaration OldD.
|
||||||
FunctionDecl *Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) {
|
FunctionDecl *
|
||||||
|
Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, bool &Redeclaration) {
|
||||||
|
Redeclaration = false;
|
||||||
// Verify the old decl was also a function.
|
// Verify the old decl was also a function.
|
||||||
FunctionDecl *Old = dyn_cast<FunctionDecl>(OldD);
|
FunctionDecl *Old = dyn_cast<FunctionDecl>(OldD);
|
||||||
if (!Old) {
|
if (!Old) {
|
||||||
|
@ -267,28 +269,31 @@ FunctionDecl *Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) {
|
||||||
return New;
|
return New;
|
||||||
}
|
}
|
||||||
|
|
||||||
MergeAttributes(New, Old);
|
|
||||||
|
|
||||||
QualType OldQType = Context.getCanonicalType(Old->getType());
|
QualType OldQType = Context.getCanonicalType(Old->getType());
|
||||||
QualType NewQType = Context.getCanonicalType(New->getType());
|
QualType NewQType = Context.getCanonicalType(New->getType());
|
||||||
|
|
||||||
// C++ [dcl.fct]p3:
|
// C++ [dcl.fct]p3:
|
||||||
// All declarations for a function shall agree exactly in both the
|
// All declarations for a function shall agree exactly in both the
|
||||||
// return type and the parameter-type-list.
|
// 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);
|
return MergeCXXFunctionDecl(New, Old);
|
||||||
|
}
|
||||||
|
|
||||||
// C: Function types need to be compatible, not identical. This handles
|
// C: Function types need to be compatible, not identical. This handles
|
||||||
// duplicate function decls like "void f(int); void f(enum X);" properly.
|
// duplicate function decls like "void f(int); void f(enum X);" properly.
|
||||||
if (!getLangOptions().CPlusPlus &&
|
if (!getLangOptions().CPlusPlus &&
|
||||||
Context.functionTypesAreCompatible(OldQType, NewQType)) {
|
Context.functionTypesAreCompatible(OldQType, NewQType)) {
|
||||||
|
MergeAttributes(New, Old);
|
||||||
|
Redeclaration = true;
|
||||||
return New;
|
return New;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A function that has already been declared has been redeclared or defined
|
// A function that has already been declared has been redeclared or defined
|
||||||
// with a different type- show appropriate diagnostic
|
// with a different type- show appropriate diagnostic
|
||||||
diag::kind PrevDiag;
|
diag::kind PrevDiag;
|
||||||
if (Old->getBody())
|
if (Old->isThisDeclarationADefinition())
|
||||||
PrevDiag = diag::err_previous_definition;
|
PrevDiag = diag::err_previous_definition;
|
||||||
else if (Old->isImplicit())
|
else if (Old->isImplicit())
|
||||||
PrevDiag = diag::err_previous_implicit_declaration;
|
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
|
// 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.
|
// are in a flat namespace, make sure we consider decls in outer scopes.
|
||||||
if (PrevDecl) {
|
if (PrevDecl) {
|
||||||
NewFD = MergeFunctionDecl(NewFD, PrevDecl);
|
bool Redeclaration = false;
|
||||||
|
NewFD = MergeFunctionDecl(NewFD, PrevDecl, Redeclaration);
|
||||||
if (NewFD == 0) return 0;
|
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;
|
New = NewFD;
|
||||||
|
|
||||||
|
@ -1177,10 +1192,11 @@ Sema::DeclTy *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Declarator &D) {
|
||||||
Decl *PrevDcl = LookupDecl(D.getIdentifier(), Decl::IDNS_Ordinary,
|
Decl *PrevDcl = LookupDecl(D.getIdentifier(), Decl::IDNS_Ordinary,
|
||||||
GlobalScope);
|
GlobalScope);
|
||||||
if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(PrevDcl)) {
|
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,
|
Diag(D.getIdentifierLoc(), diag::err_redefinition,
|
||||||
D.getIdentifier()->getName());
|
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));
|
Decl *decl = static_cast<Decl*>(ActOnDeclarator(GlobalScope, D, 0));
|
||||||
|
|
|
@ -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'}}
|
||||||
|
|
Loading…
Reference in New Issue