// 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;
: 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);
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 {
// 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;
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.
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++]);
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());
// 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())
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,
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
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) {
// 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 &&
// 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 &&
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() {}