[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