mirror of
https://github.com/intel/llvm.git
synced 2026-01-26 12:26:52 +08:00
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:
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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++]);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
Reference in New Issue
Block a user