]> granicus.if.org Git - clang/commitdiff
Fix handling of C99 "extern inline" semantics when dealing with
authorDouglas Gregor <dgregor@apple.com>
Thu, 23 Apr 2009 18:22:55 +0000 (18:22 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 23 Apr 2009 18:22:55 +0000 (18:22 +0000)
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

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

index a573c48af3311bf7ceade63e6ab963aa33866f9d..1d2c39e18a106719f53cef2f361d8c86daa86e4d 100644 (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 { 
index c07eac993bee6e96a338a698a38efbc168707d4c..378223e3a71bf9d48747cc11e9946647018b78a2 100644 (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.
index 2f6206dd094ab9bd42ce127f10b59678935c73a9..d225c68392f29463897e01ac2103ee56267a5bc3 100644 (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++]);
index 62e129919b1b8c0851c45275901410db90f1a411..0c640e7b052645ddaf51627c5cfb5731e75d26e2 100644 (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());
index c7a45dc9d70cc75129a19874667d9eda854dfead..b3592086ddd643cd1afdd81807488d08665e5708 100644 (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
index d0c0ab86e1b5accb345561af117dbf4c16830020..d05b99ac365c357fbe01f158784aafc4e7ec019b 100644 (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) {
index eb8ee718d3211ceac20c024776d95a07b316ed62..6011f42f9cf62da450401e564e06f973fc8397ca 100644 (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()  {}