Fix handling of C99 "extern inline" semantics when dealing with

multiple declarations of the function. Should fix PR3989 and
<rdar://problem/6818429>.

llvm-svn: 69905
This commit is contained in:
Douglas Gregor
2009-04-23 18:22:55 +00:00
parent a6acb390e7
commit 89c8e000cf
7 changed files with 93 additions and 11 deletions

View File

@@ -515,6 +515,7 @@ private:
// NOTE: VC++ treats enums as signed, avoid using the StorageClass enum
unsigned SClass : 2;
bool IsInline : 1;
bool C99InlineDefinition : 1;
bool IsVirtual : 1;
bool IsPure : 1;
bool InheritedPrototype : 1;
@@ -531,9 +532,9 @@ protected:
: ValueDecl(DK, DC, L, N, T),
DeclContext(DK),
ParamInfo(0), Body(), PreviousDeclaration(0),
SClass(S), IsInline(isInline), IsVirtual(false), IsPure(false),
InheritedPrototype(false), HasPrototype(true), IsDeleted(false),
TypeSpecStartLoc(TSSL) {}
SClass(S), IsInline(isInline), C99InlineDefinition(false),
IsVirtual(false), IsPure(false), InheritedPrototype(false),
HasPrototype(true), IsDeleted(false), TypeSpecStartLoc(TSSL) {}
virtual ~FunctionDecl() {}
virtual void Destroy(ASTContext& C);
@@ -679,6 +680,11 @@ public:
bool isInline() const { return IsInline; }
void setInline(bool I) { IsInline = I; }
/// \brief Whether this function is an "inline definition" as
/// defined by C99.
bool isC99InlineDefinition() const { return C99InlineDefinition; }
void setC99InlineDefinition(bool I) { C99InlineDefinition = I; }
/// isOverloadedOperator - Whether this function declaration
/// represents an C++ overloaded operator, e.g., "operator+".
bool isOverloadedOperator() const {

View File

@@ -242,7 +242,7 @@ GetLinkageForFunction(const FunctionDecl *FD, const LangOptions &Features) {
// this is C89 mode, we use to GNU semantics.
if (FD->hasAttr<GNUInlineAttr>() || (!Features.C99 && !Features.CPlusPlus)) {
// extern inline in GNU mode is like C99 inline.
if (FD->getStorageClass() == FunctionDecl::Extern)
if (FD->isC99InlineDefinition())
return CodeGenModule::GVA_C99Inline;
// Normal inline is a strong symbol.
return CodeGenModule::GVA_StrongExternal;
@@ -254,11 +254,10 @@ GetLinkageForFunction(const FunctionDecl *FD, const LangOptions &Features) {
return CodeGenModule::GVA_CXXInline;
assert(Features.C99 && "Must be in C99 mode if not in C89 or C++ mode");
// extern inline in C99 is a strong definition.
if (FD->getStorageClass() == FunctionDecl::Extern)
return CodeGenModule::GVA_StrongExternal;
return CodeGenModule::GVA_C99Inline;
if (FD->isC99InlineDefinition())
return CodeGenModule::GVA_C99Inline;
return CodeGenModule::GVA_StrongExternal;
}
/// SetFunctionDefinitionAttributes - Set attributes for a global.

View File

@@ -179,6 +179,7 @@ void PCHDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
cast_or_null<FunctionDecl>(Reader.GetDecl(Record[Idx++])));
FD->setStorageClass((FunctionDecl::StorageClass)Record[Idx++]);
FD->setInline(Record[Idx++]);
FD->setC99InlineDefinition(Record[Idx++]);
FD->setVirtual(Record[Idx++]);
FD->setPure(Record[Idx++]);
FD->setInheritedPrototype(Record[Idx++]);

View File

@@ -356,6 +356,7 @@ void PCHDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
Writer.AddDeclRef(D->getPreviousDeclaration(), Record);
Record.push_back(D->getStorageClass()); // FIXME: stable encoding
Record.push_back(D->isInline());
Record.push_back(D->isC99InlineDefinition());
Record.push_back(D->isVirtual());
Record.push_back(D->isPure());
Record.push_back(D->inheritedPrototype());

View File

@@ -833,7 +833,21 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old) {
// Merge the storage class.
New->setStorageClass(Old->getStorageClass());
// FIXME: need to implement inline semantics
// Merge "inline"
if (Old->isInline())
New->setInline(true);
// If this function declaration by itself qualifies as a C99 inline
// definition (C99 6.7.4p6), but the previous definition did not,
// then the function is not a C99 inline definition.
if (New->isC99InlineDefinition() && !Old->isC99InlineDefinition())
New->setC99InlineDefinition(false);
else if (Old->isC99InlineDefinition() && !New->isC99InlineDefinition()) {
// Mark all preceding definitions as not being C99 inline definitions.
for (const FunctionDecl *Prev = Old; Prev;
Prev = Prev->getPreviousDeclaration())
const_cast<FunctionDecl *>(Prev)->setC99InlineDefinition(false);
}
// Merge "pure" flag.
if (Old->isPure())
@@ -2177,6 +2191,19 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
isOutOfScopePreviousDeclaration(PrevDecl, DC, Context)))
PrevDecl = 0;
// FIXME: We need to determine whether the GNU inline attribute will
// be applied to this function declaration, since it affects
// declaration merging. This hack will go away when the FIXME below
// is resolved, since we should be putting *all* attributes onto the
// declaration now.
for (const AttributeList *Attr = D.getDeclSpec().getAttributes();
Attr; Attr = Attr->getNext()) {
if (Attr->getKind() == AttributeList::AT_gnu_inline) {
NewFD->addAttr(::new (Context) GNUInlineAttr());
break;
}
}
// Perform semantic checking on the function declaration.
bool OverloadableAttrRequired = false; // FIXME: HACK!
if (CheckFunctionDeclaration(NewFD, PrevDecl, Redeclaration,
@@ -2292,6 +2319,32 @@ bool Sema::CheckFunctionDeclaration(FunctionDecl *NewFD, NamedDecl *&PrevDecl,
InvalidDecl = true;
}
// C99 6.7.4p6:
// [... ] For a function with external linkage, the following
// restrictions apply: [...] If all of the file scope declarations
// for a function in a translation unit include the inline
// function specifier without extern, then the definition in that
// translation unit is an inline definition. An inline definition
// does not provide an external definition for the function, and
// does not forbid an external definition in another translation
// unit.
//
// Here we determine whether this function, in isolation, would be a
// C99 inline definition. MergeCompatibleFunctionDecls looks at
// previous declarations.
if (NewFD->isInline() &&
NewFD->getDeclContext()->getLookupContext()->isTranslationUnit()) {
bool GNUInline = NewFD->hasAttr<GNUInlineAttr>() ||
(PrevDecl && PrevDecl->hasAttr<GNUInlineAttr>());
if (GNUInline || (!getLangOptions().CPlusPlus && !getLangOptions().C99)) {
// GNU "extern inline" is the same as "inline" in C99.
if (NewFD->getStorageClass() == FunctionDecl::Extern)
NewFD->setC99InlineDefinition(true);
} else if (getLangOptions().C99 &&
NewFD->getStorageClass() == FunctionDecl::None)
NewFD->setC99InlineDefinition(true);
}
// Check for a previous declaration of this name.
if (!PrevDecl && NewFD->isExternC(Context)) {
// Since we did not find anything by this name and we're declaring

View File

@@ -1474,7 +1474,11 @@ static void HandleGNUInlineAttr(Decl *d, const AttributeList &Attr, Sema &S) {
return;
}
d->addAttr(::new (S.Context) GNUInlineAttr());
// FIXME: We only do this because of the hack in
// Sema::ActOnFunctionDeclarator, which needs to add the
// GNUInlineAttr early.
if (!d->hasAttr<GNUInlineAttr>())
d->addAttr(::new (S.Context) GNUInlineAttr());
}
static void HandleRegparmAttr(Decl *d, const AttributeList &Attr, Sema &S) {

View File

@@ -7,6 +7,9 @@
// RUN: not grep unreferenced2 %t &&
// RUN: grep "define void @gnu_inline()" %t &&
// RUN: grep "define available_externally void @gnu_ei_inline()" %t &&
// RUN: grep "define void @test3()" %t &&
// RUN: grep "define i32 @test1" %t &&
// RUN: grep "define i32 @test2" %t &&
// RUN: echo "\nC99 tests:" &&
// RUN: clang %s -emit-llvm -S -o %t -std=c99 &&
@@ -17,6 +20,8 @@
// RUN: grep "define void @unreferenced2()" %t &&
// RUN: grep "define void @gnu_inline()" %t &&
// RUN: grep "define available_externally void @gnu_ei_inline()" %t &&
// RUN: grep "define i32 @test1" %t &&
// RUN: grep "define i32 @test2" %t &&
// RUN: echo "\nC++ tests:" &&
// RUN: clang %s -emit-llvm -S -o %t -std=c++98 &&
@@ -45,3 +50,16 @@ __inline __attribute((__gnu_inline__)) void gnu_inline() {}
extern inline __attribute__((gnu_inline)) void gnu_ei_inline() {}
void (*P)() = gnu_ei_inline;
// <rdar://problem/6818429>
int test1();
inline int test1() { return 4; }
inline int test2() { return 5; }
inline int test2();
int test2();
void test_test1() { test1(); }
void test_test2() { test2(); }
// PR3989
extern inline void test3() __attribute__((gnu_inline));
inline void test3() {}