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
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index c7a45dc..b359208 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -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
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index d0c0ab8..d05b99a 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -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) {