[cfe-commits] r69905 - in /cfe/trunk: include/clang/AST/Decl.h lib/CodeGen/CodeGenModule.cpp lib/Frontend/PCHReader.cpp lib/Frontend/PCHWriter.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclAttr.cpp test/CodeGen/inline.c
Douglas Gregor
dgregor at apple.com
Thu Apr 23 11:22:56 PDT 2009
Author: dgregor
Date: Thu Apr 23 13:22:55 2009
New Revision: 69905
URL: http://llvm.org/viewvc/llvm-project?rev=69905&view=rev
Log:
Fix handling of C99 "extern inline" semantics when dealing with
multiple declarations of the function. Should fix PR3989 and
<rdar://problem/6818429>.
Modified:
cfe/trunk/include/clang/AST/Decl.h
cfe/trunk/lib/CodeGen/CodeGenModule.cpp
cfe/trunk/lib/Frontend/PCHReader.cpp
cfe/trunk/lib/Frontend/PCHWriter.cpp
cfe/trunk/lib/Sema/SemaDecl.cpp
cfe/trunk/lib/Sema/SemaDeclAttr.cpp
cfe/trunk/test/CodeGen/inline.c
Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=69905&r1=69904&r2=69905&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Thu Apr 23 13:22:55 2009
@@ -515,6 +515,7 @@
// 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 @@
: 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 @@
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 {
Modified: cfe/trunk/lib/CodeGen/CodeGenModule.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=69905&r1=69904&r2=69905&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenModule.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp Thu Apr 23 13:22:55 2009
@@ -242,7 +242,7 @@
// 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 @@
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.
Modified: cfe/trunk/lib/Frontend/PCHReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHReader.cpp?rev=69905&r1=69904&r2=69905&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/PCHReader.cpp (original)
+++ cfe/trunk/lib/Frontend/PCHReader.cpp Thu Apr 23 13:22:55 2009
@@ -179,6 +179,7 @@
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++]);
Modified: cfe/trunk/lib/Frontend/PCHWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHWriter.cpp?rev=69905&r1=69904&r2=69905&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/PCHWriter.cpp (original)
+++ cfe/trunk/lib/Frontend/PCHWriter.cpp Thu Apr 23 13:22:55 2009
@@ -356,6 +356,7 @@
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());
Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=69905&r1=69904&r2=69905&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Thu Apr 23 13:22:55 2009
@@ -833,7 +833,21 @@
// 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 @@
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 @@
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
Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=69905&r1=69904&r2=69905&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Thu Apr 23 13:22:55 2009
@@ -1474,7 +1474,11 @@
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) {
Modified: cfe/trunk/test/CodeGen/inline.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/inline.c?rev=69905&r1=69904&r2=69905&view=diff
==============================================================================
--- cfe/trunk/test/CodeGen/inline.c (original)
+++ cfe/trunk/test/CodeGen/inline.c Thu Apr 23 13:22:55 2009
@@ -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 @@
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() {}
More information about the cfe-commits
mailing list