aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2008-04-21 02:02:58 +0000
committerDouglas Gregor <dgregor@apple.com>2008-04-21 02:02:58 +0000
commitf009795057dc8ca254f5618c80a0a90f07cd44b4 (patch)
tree5329561e54fb30356a7b47e6827b3021c5d2047c /lib
parent7b989570f0fe262b6bdbad412577450c81304936 (diff)
Clean up handling of function redeclarations
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@50021 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r--lib/AST/Decl.cpp77
-rw-r--r--lib/Sema/Sema.h3
-rw-r--r--lib/Sema/SemaDecl.cpp34
3 files changed, 104 insertions, 10 deletions
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 6ebaac569f..11e6bcfb0f 100644
--- a/lib/AST/Decl.cpp
+++ b/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/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 6d6a26d95d..e70da932f6 100644
--- a/lib/Sema/Sema.h
+++ b/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/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index eec8ce5d0a..415908a30b 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/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));