aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/AST/ASTContext.h3
-rw-r--r--include/clang/AST/Attr.h330
-rw-r--r--include/clang/AST/Decl.h13
-rw-r--r--lib/AST/Decl.cpp24
-rw-r--r--lib/CodeGen/CodeGenModule.cpp12
-rw-r--r--lib/Sema/SemaDecl.cpp51
-rw-r--r--lib/Sema/SemaDeclAttr.cpp6
-rw-r--r--test/CodeGen/inline.c25
8 files changed, 179 insertions, 285 deletions
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index 4379854c76..f4a01c4cf0 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -16,6 +16,7 @@
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LangOptions.h"
+#include "clang/AST/Attr.h"
#include "clang/AST/Builtins.h"
#include "clang/AST/Decl.h"
#include "clang/AST/NestedNameSpecifier.h"
@@ -768,7 +769,7 @@ private:
/// allocator supports it).
/// @return The allocated memory. Could be NULL.
inline void *operator new(size_t Bytes, clang::ASTContext &C,
- size_t Alignment = 16) throw () {
+ size_t Alignment) throw () {
return C.Allocate(Bytes, Alignment);
}
/// @brief Placement delete companion to the new above.
diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h
index 91abcffdcd..badab47911 100644
--- a/include/clang/AST/Attr.h
+++ b/include/clang/AST/Attr.h
@@ -21,6 +21,14 @@
namespace clang {
class ASTContext;
+}
+
+
+// Defined in ASTContext.cpp
+void *operator new(size_t Bytes, clang::ASTContext &C,
+ size_t Alignment = 16) throw ();
+
+namespace clang {
/// Attr - This represents one attribute.
class Attr {
@@ -116,10 +124,22 @@ public:
Next = attr;
}
+ // Clone this attribute.
+ virtual Attr* clone(ASTContext &C) const = 0;
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Attr *) { return true; }
};
+#define DEF_SIMPLE_ATTR(ATTR) \
+class ATTR##Attr : public Attr { \
+public: \
+ ATTR##Attr() : Attr(ATTR) {} \
+ virtual Attr *clone(ASTContext &C) const { return ::new (C) ATTR##Attr; } \
+ static bool classof(const Attr *A) { return A->getKind() == ATTR; } \
+ static bool classof(const ATTR##Attr *A) { return true; } \
+}
+
class PackedAttr : public Attr {
unsigned Alignment;
@@ -129,6 +149,10 @@ public:
/// getAlignment - The specified alignment in bits.
unsigned getAlignment() const { return Alignment; }
+ virtual Attr* clone(ASTContext &C) const {
+ return ::new (C) PackedAttr(Alignment);
+ }
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) {
return A->getKind() == Packed;
@@ -143,6 +167,8 @@ public:
/// getAlignment - The specified alignment in bits.
unsigned getAlignment() const { return Alignment; }
+
+ virtual Attr* clone(ASTContext &C) const { return ::new (C) AlignedAttr(Alignment); }
// Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) {
@@ -157,6 +183,8 @@ public:
AnnotateAttr(const std::string &ann) : Attr(Annotate), Annotation(ann) {}
const std::string& getAnnotation() const { return Annotation; }
+
+ virtual Attr* clone(ASTContext &C) const { return ::new (C) AnnotateAttr(Annotation); }
// Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) {
@@ -172,6 +200,8 @@ public:
const std::string& getLabel() const { return Label; }
+ virtual Attr* clone(ASTContext &C) const { return ::new (C) AsmLabelAttr(Label); }
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) {
return A->getKind() == AsmLabel;
@@ -179,15 +209,7 @@ public:
static bool classof(const AsmLabelAttr *A) { return true; }
};
-class AlwaysInlineAttr : public Attr {
-public:
- AlwaysInlineAttr() : Attr(AlwaysInline) {}
-
- // Implement isa/cast/dyncast/etc.
-
- static bool classof(const Attr *A) { return A->getKind() == AlwaysInline; }
- static bool classof(const AlwaysInlineAttr *A) { return true; }
-};
+DEF_SIMPLE_ATTR(AlwaysInline);
class AliasAttr : public Attr {
std::string Aliasee;
@@ -196,8 +218,9 @@ public:
const std::string& getAliasee() const { return Aliasee; }
- // Implement isa/cast/dyncast/etc.
+ virtual Attr *clone(ASTContext &C) const { return ::new (C) AliasAttr(Aliasee); }
+ // Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) { return A->getKind() == Alias; }
static bool classof(const AliasAttr *A) { return true; }
};
@@ -209,6 +232,8 @@ public:
int getPriority() const { return priority; }
+ virtual Attr *clone(ASTContext &C) const { return ::new (C) ConstructorAttr(priority); }
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) { return A->getKind() == Constructor; }
static bool classof(const ConstructorAttr *A) { return true; }
@@ -221,25 +246,32 @@ public:
int getPriority() const { return priority; }
+ virtual Attr *clone(ASTContext &C) const { return ::new (C) DestructorAttr(priority); }
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) { return A->getKind() == Destructor; }
static bool classof(const DestructorAttr *A) { return true; }
};
-
class GNUInlineAttr : public Attr {
public:
GNUInlineAttr() : Attr(GNUInline) {}
-
+
+ virtual Attr *clone(ASTContext &C) const { return ::new (C) GNUInlineAttr; }
+
// Implement isa/cast/dyncast/etc.
- static bool classof(const Attr *A) { return A->getKind() == GNUInline; }
+ static bool classof(const Attr *A) {
+ return A->getKind() == GNUInline;
+ }
static bool classof(const GNUInlineAttr *A) { return true; }
};
class IBOutletAttr : public Attr {
public:
IBOutletAttr() : Attr(IBOutletKind) {}
-
+
+ virtual Attr *clone(ASTContext &C) const { return ::new (C) IBOutletAttr; }
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) {
return A->getKind() == IBOutletKind;
@@ -247,34 +279,9 @@ public:
static bool classof(const IBOutletAttr *A) { return true; }
};
-class NoReturnAttr : public Attr {
-public:
- NoReturnAttr() : Attr(NoReturn) {}
-
- // Implement isa/cast/dyncast/etc.
- static bool classof(const Attr *A) { return A->getKind() == NoReturn; }
- static bool classof(const NoReturnAttr *A) { return true; }
-};
-
-class AnalyzerNoReturnAttr : public Attr {
-public:
- AnalyzerNoReturnAttr() : Attr(AnalyzerNoReturn) {}
-
- // Implement isa/cast/dyncast/etc.
- static bool classof(const Attr *A) {
- return A->getKind() == AnalyzerNoReturn;
- }
- static bool classof(const AnalyzerNoReturnAttr *A) { return true; }
-};
-
-class DeprecatedAttr : public Attr {
-public:
- DeprecatedAttr() : Attr(Deprecated) {}
-
- // Implement isa/cast/dyncast/etc.
- static bool classof(const Attr *A) { return A->getKind() == Deprecated; }
- static bool classof(const DeprecatedAttr *A) { return true; }
-};
+DEF_SIMPLE_ATTR(NoReturn);
+DEF_SIMPLE_ATTR(AnalyzerNoReturn);
+DEF_SIMPLE_ATTR(Deprecated);
class SectionAttr : public Attr {
std::string Name;
@@ -282,7 +289,9 @@ public:
SectionAttr(const std::string &N) : Attr(Section), Name(N) {}
const std::string& getName() const { return Name; }
-
+
+ virtual Attr *clone(ASTContext &C) const { return ::new (C) SectionAttr(Name); }
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) {
return A->getKind() == Section;
@@ -290,80 +299,14 @@ public:
static bool classof(const SectionAttr *A) { return true; }
};
-class UnavailableAttr : public Attr {
-public:
- UnavailableAttr() : Attr(Unavailable) {}
-
- // Implement isa/cast/dyncast/etc.
-
- static bool classof(const Attr *A) { return A->getKind() == Unavailable; }
- static bool classof(const UnavailableAttr *A) { return true; }
-};
-
-class UnusedAttr : public Attr {
-public:
- UnusedAttr() : Attr(Unused) {}
-
- // Implement isa/cast/dyncast/etc.
- static bool classof(const Attr *A) { return A->getKind() == Unused; }
- static bool classof(const UnusedAttr *A) { return true; }
-};
-
-class UsedAttr : public Attr {
-public:
- UsedAttr() : Attr(Used) {}
-
- // Implement isa/cast/dyncast/etc.
- static bool classof(const Attr *A) { return A->getKind() == Used; }
- static bool classof(const UsedAttr *A) { return true; }
-};
-
-class WeakAttr : public Attr {
-public:
- WeakAttr() : Attr(Weak) {}
-
- // Implement isa/cast/dyncast/etc.
-
- static bool classof(const Attr *A) { return A->getKind() == Weak; }
- static bool classof(const WeakAttr *A) { return true; }
-};
-
-class WeakImportAttr : public Attr {
-public:
- WeakImportAttr() : Attr(WeakImport) {}
-
- // Implement isa/cast/dyncast/etc.
-
- static bool classof(const Attr *A) { return A->getKind() == WeakImport; }
- static bool classof(const WeakImportAttr *A) { return true; }
-};
-
-class NoThrowAttr : public Attr {
-public:
- NoThrowAttr() : Attr(NoThrow) {}
-
- // Implement isa/cast/dyncast/etc.
- static bool classof(const Attr *A) { return A->getKind() == NoThrow; }
- static bool classof(const NoThrowAttr *A) { return true; }
-};
-
-class ConstAttr : public Attr {
-public:
- ConstAttr() : Attr(Const) {}
-
- // Implement isa/cast/dyncast/etc.
- static bool classof(const Attr *A) { return A->getKind() == Const; }
- static bool classof(const ConstAttr *A) { return true; }
-};
-
-class PureAttr : public Attr {
-public:
- PureAttr() : Attr(Pure) {}
-
- // Implement isa/cast/dyncast/etc.
- static bool classof(const Attr *A) { return A->getKind() == Pure; }
- static bool classof(const PureAttr *A) { return true; }
-};
+DEF_SIMPLE_ATTR(Unavailable);
+DEF_SIMPLE_ATTR(Unused);
+DEF_SIMPLE_ATTR(Used);
+DEF_SIMPLE_ATTR(Weak);
+DEF_SIMPLE_ATTR(WeakImport);
+DEF_SIMPLE_ATTR(NoThrow);
+DEF_SIMPLE_ATTR(Const);
+DEF_SIMPLE_ATTR(Pure);
class NonNullAttr : public Attr {
unsigned* ArgNums;
@@ -391,7 +334,9 @@ public:
bool isNonNull(unsigned arg) const {
return ArgNums ? std::binary_search(ArgNums, ArgNums+Size, arg) : true;
}
-
+
+ virtual Attr *clone(ASTContext &C) const { return ::new (C) NonNullAttr(ArgNums, Size); }
+
static bool classof(const Attr *A) { return A->getKind() == NonNull; }
static bool classof(const NonNullAttr *A) { return true; }
};
@@ -408,8 +353,11 @@ public:
int getFormatIdx() const { return formatIdx; }
int getFirstArg() const { return firstArg; }
- // Implement isa/cast/dyncast/etc.
+ virtual Attr *clone(ASTContext &C) const {
+ return ::new (C) FormatAttr(Type, formatIdx, firstArg);
+ }
+ // Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) { return A->getKind() == Format; }
static bool classof(const FormatAttr *A) { return true; }
};
@@ -430,88 +378,29 @@ public:
VisibilityTypes getVisibility() const { return VisibilityType; }
- // Implement isa/cast/dyncast/etc.
+ virtual Attr *clone(ASTContext &C) const { return ::new (C) VisibilityAttr(VisibilityType); }
+ // Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) { return A->getKind() == Visibility; }
static bool classof(const VisibilityAttr *A) { return true; }
};
-class DLLImportAttr : public Attr {
-public:
- DLLImportAttr() : Attr(DLLImport) {}
-
- // Implement isa/cast/dyncast/etc.
-
- static bool classof(const Attr *A) { return A->getKind() == DLLImport; }
- static bool classof(const DLLImportAttr *A) { return true; }
-};
-
-class DLLExportAttr : public Attr {
-public:
- DLLExportAttr() : Attr(DLLExport) {}
-
- // Implement isa/cast/dyncast/etc.
-
- static bool classof(const Attr *A) { return A->getKind() == DLLExport; }
- static bool classof(const DLLExportAttr *A) { return true; }
-};
-
-class FastCallAttr : public Attr {
-public:
- FastCallAttr() : Attr(FastCall) {}
-
- // Implement isa/cast/dyncast/etc.
-
- static bool classof(const Attr *A) { return A->getKind() == FastCall; }
- static bool classof(const FastCallAttr *A) { return true; }
-};
-
-class StdCallAttr : public Attr {
-public:
- StdCallAttr() : Attr(StdCall) {}
-
- // Implement isa/cast/dyncast/etc.
-
- static bool classof(const Attr *A) { return A->getKind() == StdCall; }
- static bool classof(const StdCallAttr *A) { return true; }
-};
-
-class TransparentUnionAttr : public Attr {
-public:
- TransparentUnionAttr() : Attr(TransparentUnion) {}
-
- // Implement isa/cast/dyncast/etc.
+DEF_SIMPLE_ATTR(DLLImport);
+DEF_SIMPLE_ATTR(DLLExport);
+DEF_SIMPLE_ATTR(FastCall);
+DEF_SIMPLE_ATTR(StdCall);
+DEF_SIMPLE_ATTR(TransparentUnion);
+DEF_SIMPLE_ATTR(ObjCNSObject);
+DEF_SIMPLE_ATTR(ObjCException);
- static bool classof(const Attr *A) { return A->getKind() == TransparentUnion; }
- static bool classof(const TransparentUnionAttr *A) { return true; }
-};
-
-class ObjCNSObjectAttr : public Attr {
-// Implement isa/cast/dyncast/etc.
-public:
- ObjCNSObjectAttr() : Attr(ObjCNSObject) {}
-
-static bool classof(const Attr *A) { return A->getKind() == ObjCNSObject; }
-static bool classof(const ObjCNSObjectAttr *A) { return true; }
-};
-
-
-class ObjCExceptionAttr : public Attr {
-public:
- ObjCExceptionAttr() : Attr(ObjCException) {}
-
- // Implement isa/cast/dyncast/etc.
- static bool classof(const Attr *A) { return A->getKind() == ObjCException; }
- static bool classof(const ObjCExceptionAttr *A) { return true; }
-};
-
-
class OverloadableAttr : public Attr {
public:
OverloadableAttr() : Attr(Overloadable) { }
virtual bool isMerged() const { return false; }
+ virtual Attr *clone(ASTContext &C) const { return ::new (C) OverloadableAttr; }
+
static bool classof(const Attr *A) { return A->getKind() == Overloadable; }
static bool classof(const OverloadableAttr *) { return true; }
};
@@ -528,8 +417,9 @@ public:
BlocksAttrTypes getType() const { return BlocksAttrType; }
- // Implement isa/cast/dyncast/etc.
+ virtual Attr *clone(ASTContext &C) const { return ::new (C) BlocksAttr(BlocksAttrType); }
+ // Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) { return A->getKind() == Blocks; }
static bool classof(const BlocksAttr *A) { return true; }
};
@@ -544,40 +434,16 @@ public:
const FunctionDecl *getFunctionDecl() const { return FD; }
- // Implement isa/cast/dyncast/etc.
+ virtual Attr *clone(ASTContext &C) const { return ::new (C) CleanupAttr(FD); }
+ // Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) { return A->getKind() == Cleanup; }
static bool classof(const CleanupAttr *A) { return true; }
};
-class NodebugAttr : public Attr {
-public:
- NodebugAttr() : Attr(Nodebug) {}
-
- // Implement isa/cast/dyncast/etc.
-
- static bool classof(const Attr *A) { return A->getKind() == Nodebug; }
- static bool classof(const NodebugAttr *A) { return true; }
-};
-
-class WarnUnusedResultAttr : public Attr {
-public:
- WarnUnusedResultAttr() : Attr(WarnUnusedResult) {}
-
- // Implement isa/cast/dyncast/etc.
- static bool classof(const Attr *A) { return A->getKind() == WarnUnusedResult;}
- static bool classof(const WarnUnusedResultAttr *A) { return true; }
-};
-
-class NoinlineAttr : public Attr {
-public:
- NoinlineAttr() : Attr(Noinline) {}
-
- // Implement isa/cast/dyncast/etc.
-
- static bool classof(const Attr *A) { return A->getKind() == Noinline; }
- static bool classof(const NoinlineAttr *A) { return true; }
-};
+DEF_SIMPLE_ATTR(Nodebug);
+DEF_SIMPLE_ATTR(WarnUnusedResult);
+DEF_SIMPLE_ATTR(Noinline);
class RegparmAttr : public Attr {
unsigned NumParams;
@@ -587,27 +453,21 @@ public:
unsigned getNumParams() const { return NumParams; }
- // Implement isa/cast/dyncast/etc.
-
+ virtual Attr *clone(ASTContext &C) const {
+ return ::new (C) RegparmAttr(NumParams);
+ }
+
+ // Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) { return A->getKind() == Regparm; }
static bool classof(const RegparmAttr *A) { return true; }
};
-
-
-#define DEF_SIMPLE_ATTR(ATTR)\
-class ATTR##Attr : public Attr {\
-public:\
- ATTR##Attr() : Attr(ATTR) {}\
- static bool classof(const Attr *A) { return A->getKind() == ATTR; }\
- static bool classof(const ATTR##Attr *A) { return true; }\
-};
// Checker-specific attributes.
-DEF_SIMPLE_ATTR(ObjCOwnershipCFRelease)
-DEF_SIMPLE_ATTR(ObjCOwnershipRelease)
-DEF_SIMPLE_ATTR(ObjCOwnershipCFRetain)
-DEF_SIMPLE_ATTR(ObjCOwnershipRetain)
-DEF_SIMPLE_ATTR(ObjCOwnershipReturns)
+DEF_SIMPLE_ATTR(ObjCOwnershipCFRelease);
+DEF_SIMPLE_ATTR(ObjCOwnershipRelease);
+DEF_SIMPLE_ATTR(ObjCOwnershipCFRetain);
+DEF_SIMPLE_ATTR(ObjCOwnershipRetain);
+DEF_SIMPLE_ATTR(ObjCOwnershipReturns);
#undef DEF_SIMPLE_ATTR
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index 9cd22eeb3b..90d1328c3d 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -690,6 +690,19 @@ public:
bool isC99InlineDefinition() const { return C99InlineDefinition; }
void setC99InlineDefinition(bool I) { C99InlineDefinition = I; }
+ /// \brief Determines whether this function has a gnu_inline
+ /// attribute that affects its semantics.
+ ///
+ /// The gnu_inline attribute only introduces GNU inline semantics
+ /// when all of the inline declarations of the function are marked
+ /// gnu_inline.
+ bool hasActiveGNUInlineAttribute() const;
+
+ /// \brief Determines whether this function is a GNU "extern
+ /// inline", which is roughly the opposite of a C99 "extern inline"
+ /// function.
+ bool isExternGNUInline() const;
+
/// isOverloadedOperator - Whether this function declaration
/// represents an C++ overloaded operator, e.g., "operator+".
bool isOverloadedOperator() const {
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index aa0fc702a4..9aba33c943 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -468,6 +468,30 @@ unsigned FunctionDecl::getMinRequiredArguments() const {
return NumRequiredArgs;
}
+bool FunctionDecl::hasActiveGNUInlineAttribute() const {
+ if (!isInline() || !hasAttr<GNUInlineAttr>())
+ return false;
+
+ for (const FunctionDecl *FD = getPreviousDeclaration(); FD;
+ FD = FD->getPreviousDeclaration()) {
+ if (FD->isInline() && !FD->hasAttr<GNUInlineAttr>())
+ return false;
+ }
+
+ return true;
+}
+
+bool FunctionDecl::isExternGNUInline() const {
+ if (!hasActiveGNUInlineAttribute())
+ return false;
+
+ for (const FunctionDecl *FD = this; FD; FD = FD->getPreviousDeclaration())
+ if (FD->getStorageClass() == Extern && FD->hasAttr<GNUInlineAttr>())
+ return true;
+
+ return false;
+}
+
/// getOverloadedOperator - Which C++ overloaded operator this
/// function represents, if any.
OverloadedOperatorKind FunctionDecl::getOverloadedOperator() const {
diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp
index 378223e3a7..4e58d09360 100644
--- a/lib/CodeGen/CodeGenModule.cpp
+++ b/lib/CodeGen/CodeGenModule.cpp
@@ -240,9 +240,17 @@ GetLinkageForFunction(const FunctionDecl *FD, const LangOptions &Features) {
// If the inline function explicitly has the GNU inline attribute on it, or if
// this is C89 mode, we use to GNU semantics.
- if (FD->hasAttr<GNUInlineAttr>() || (!Features.C99 && !Features.CPlusPlus)) {
+ if (!Features.C99 && !Features.CPlusPlus) {
// extern inline in GNU mode is like C99 inline.
- if (FD->isC99InlineDefinition())
+ if (FD->getStorageClass() == FunctionDecl::Extern)
+ return CodeGenModule::GVA_C99Inline;
+ // Normal inline is a strong symbol.
+ return CodeGenModule::GVA_StrongExternal;
+ } else if (FD->hasActiveGNUInlineAttribute()) {
+ // GCC in C99 mode seems to use a different decision-making
+ // process for extern inline, which factors in previous
+ // declarations.
+ if (FD->isExternGNUInline())
return CodeGenModule::GVA_C99Inline;
// Normal inline is a strong symbol.
return CodeGenModule::GVA_StrongExternal;
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index b46dd5547f..4427f0de1f 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -584,22 +584,13 @@ static bool DeclHasAttr(const Decl *decl, const Attr *target) {
/// MergeAttributes - append attributes from the Old decl to the New one.
static void MergeAttributes(Decl *New, Decl *Old, ASTContext &C) {
- Attr *attr = const_cast<Attr*>(Old->getAttrs());
-
- while (attr) {
- Attr *tmp = attr;
- attr = attr->getNext();
-
- if (!DeclHasAttr(New, tmp) && tmp->isMerged()) {
- tmp->setInherited(true);
- New->addAttr(tmp);
- } else {
- tmp->setNext(0);
- tmp->Destroy(C);
+ for (const Attr *attr = Old->getAttrs(); attr; attr = attr->getNext()) {
+ if (!DeclHasAttr(New, attr) && attr->isMerged()) {
+ Attr *NewAttr = attr->clone(C);
+ NewAttr->setInherited(true);
+ New->addAttr(NewAttr);
}
}
-
- Old->invalidateAttrs();
}
/// Used in MergeFunctionDecl to keep track of function parameters in
@@ -851,7 +842,8 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old) {
MergeAttributes(New, Old, Context);
// Merge the storage class.
- New->setStorageClass(Old->getStorageClass());
+ if (Old->getStorageClass() != FunctionDecl::Extern)
+ New->setStorageClass(Old->getStorageClass());
// Merge "inline"
if (Old->isInline())
@@ -2186,19 +2178,6 @@ 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!
CheckFunctionDeclaration(NewFD, PrevDecl, Redeclaration,
@@ -2328,18 +2307,10 @@ void Sema::CheckFunctionDeclaration(FunctionDecl *NewFD, NamedDecl *&PrevDecl,
// 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);
- }
+ if (NewFD->isInline() && getLangOptions().C99 &&
+ NewFD->getStorageClass() == FunctionDecl::None &&
+ NewFD->getDeclContext()->getLookupContext()->isTranslationUnit())
+ NewFD->setC99InlineDefinition(true);
// Check for a previous declaration of this name.
if (!PrevDecl && NewFD->isExternC(Context)) {
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index c72b7ad7e8..09c627e42d 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -1474,11 +1474,7 @@ static void HandleGNUInlineAttr(Decl *d, const AttributeList &Attr, Sema &S) {
return;
}
- // 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());
+ d->addAttr(::new (S.Context) GNUInlineAttr());
}
static void HandleRegparmAttr(Decl *d, const AttributeList &Attr, Sema &S) {
diff --git a/test/CodeGen/inline.c b/test/CodeGen/inline.c
index 7bdf76de19..234f1f8d93 100644
--- a/test/CodeGen/inline.c
+++ b/test/CodeGen/inline.c
@@ -7,9 +7,11 @@
// 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: grep "define void @test3()" %t &&
+// RUN: grep "define available_externally i32 @test4" %t &&
+// RUN: grep "define available_externally i32 @test5" %t &&
// RUN: echo "\nC99 tests:" &&
// RUN: clang %s -emit-llvm -S -o %t -std=c99 &&
@@ -22,6 +24,9 @@
// RUN: grep "define available_externally void @gnu_ei_inline()" %t &&
// RUN: grep "define i32 @test1" %t &&
// RUN: grep "define i32 @test2" %t &&
+// RUN: grep "define available_externally void @test3" %t &&
+// RUN: grep "define available_externally i32 @test4" %t &&
+// RUN: grep "define i32 @test5" %t &&
// RUN: echo "\nC++ tests:" &&
// RUN: clang %s -emit-llvm -S -o %t -std=c++98 &&
@@ -62,4 +67,20 @@ void test_test2() { test2(); }
// PR3989
extern __inline void test3() __attribute__((gnu_inline));
-__inline void test3() {}
+__inline void test3() {}
+
+void test_test3() { test3(); }
+
+extern int test4(void);
+extern __inline __attribute__ ((__gnu_inline__)) int test4(void)
+{
+}
+
+void test_test4() { test4(); }
+
+extern __inline int test5(void);
+extern __inline int __attribute__ ((__gnu_inline__)) test5(void)
+{
+}
+
+void test_test5() { test5(); }