diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-04-23 18:22:55 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-04-23 18:22:55 +0000 |
commit | b3efa98e320590e8be9d62818e89e599303e65b4 (patch) | |
tree | e1dfe49a1b96f17d9dc5815273bee027ed5d5343 /lib | |
parent | 87de6497f45243fbcef6dac5be6a641ca9331f61 (diff) |
Fix handling of C99 "extern inline" semantics when dealing with
multiple declarations of the function. Should fix PR3989 and
<rdar://problem/6818429>.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69905 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r-- | lib/CodeGen/CodeGenModule.cpp | 11 | ||||
-rw-r--r-- | lib/Frontend/PCHReader.cpp | 1 | ||||
-rw-r--r-- | lib/Frontend/PCHWriter.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 55 | ||||
-rw-r--r-- | lib/Sema/SemaDeclAttr.cpp | 6 |
5 files changed, 66 insertions, 8 deletions
diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index c07eac993b..378223e3a7 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -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. diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp index 2f6206dd09..d225c68392 100644 --- a/lib/Frontend/PCHReader.cpp +++ b/lib/Frontend/PCHReader.cpp @@ -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++]); diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp index 62e129919b..0c640e7b05 100644 --- a/lib/Frontend/PCHWriter.cpp +++ b/lib/Frontend/PCHWriter.cpp @@ -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()); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c7a45dc9d7..b3592086dd 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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 diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index d0c0ab86e1..d05b99ac36 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -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) { |