In C99, emit an inline function when encountering an extern redeclaration.

Fixes PR10233!

llvm-svn: 135377
This commit is contained in:
Nick Lewycky 2011-07-18 05:26:13 +00:00
parent 14b3b4df6f
commit 26da4ddfa6
5 changed files with 51 additions and 3 deletions

View File

@ -1773,6 +1773,8 @@ public:
bool isInlined() const;
bool isInlineDefinitionExternallyVisible() const;
bool doesDeclarationForceExternallyVisibleDefinition() const;
/// isOverloadedOperator - Whether this function declaration
/// represents an C++ overloaded operator, e.g., "operator+".

View File

@ -6405,7 +6405,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
// Forward declarations aren't required.
if (!FD->doesThisDeclarationHaveABody())
return false;
return FD->doesDeclarationForceExternallyVisibleDefinition();
// Constructors and destructors are required.
if (FD->hasAttr<ConstructorAttr>() || FD->hasAttr<DestructorAttr>())

View File

@ -1762,6 +1762,32 @@ bool FunctionDecl::isInlined() const {
return false;
}
/// \brief For a function declaration in C or C++, determine whether this
/// declaration causes the definition to be externally visible.
///
/// Determines whether this is the first non-inline redeclaration of an inline
/// function in a language where "inline" does not normally require an
/// externally visible definition.
bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const {
assert(!doesThisDeclarationHaveABody() &&
"Must have a declaration without a body.");
ASTContext &Context = getASTContext();
// In C99 mode, a function may have an inline definition (causing it to
// be deferred) then redeclared later. As a special case, "extern inline"
// is not required to produce an external symbol.
if (Context.getLangOptions().GNUInline || !Context.getLangOptions().C99 ||
Context.getLangOptions().CPlusPlus)
return false;
if (getLinkage() != ExternalLinkage || isInlineSpecified())
return false;
const FunctionDecl *InlineDefinition = 0;
if (hasBody(InlineDefinition))
return InlineDefinition->isInlineDefinitionExternallyVisible();
return false;
}
/// \brief For an inline function definition in C or C++, determine whether the
/// definition will be externally visible.
///

View File

@ -597,7 +597,7 @@ void CodeGenModule::EmitLLVMUsed() {
void CodeGenModule::EmitDeferred() {
// Emit code for any potentially referenced deferred decls. Since a
// previously unused static decl may become used during the generation of code
// for a static function, iterate until no changes are made.
// for a static function, iterate until no changes are made.
while (!DeferredDeclsToEmit.empty() || !DeferredVTables.empty()) {
if (!DeferredVTables.empty()) {
@ -740,8 +740,21 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
}
// Forward declarations are emitted lazily on first use.
if (!FD->doesThisDeclarationHaveABody())
if (!FD->doesThisDeclarationHaveABody()) {
if (!FD->doesDeclarationForceExternallyVisibleDefinition())
return;
const FunctionDecl *InlineDefinition = 0;
FD->getBody(InlineDefinition);
llvm::StringRef MangledName = getMangledName(GD);
llvm::StringMap<GlobalDecl>::iterator DDI =
DeferredDecls.find(MangledName);
if (DDI != DeferredDecls.end())
DeferredDecls.erase(DDI);
EmitGlobalDefinition(InlineDefinition);
return;
}
} else {
const VarDecl *VD = cast<VarDecl>(Global);
assert(VD->isFileVarDecl() && "Cannot emit local var decl as global.");

View File

@ -12,6 +12,7 @@
// RUN: grep "define void @test3()" %t
// RUN: grep "define available_externally i32 @test4" %t
// RUN: grep "define available_externally i32 @test5" %t
// RUN: grep "define i32 @test6" %t
// RUN: echo "\nC99 tests:"
// RUN: %clang %s -O1 -emit-llvm -S -o %t -std=c99
@ -27,6 +28,7 @@
// RUN: grep "define void @test3" %t
// RUN: grep "define available_externally i32 @test4" %t
// RUN: grep "define available_externally i32 @test5" %t
// RUN: grep "define i32 @test6" %t
// RUN: echo "\nC++ tests:"
// RUN: %clang -x c++ %s -O1 -emit-llvm -S -o %t -std=c++98
@ -84,3 +86,8 @@ extern __inline int __attribute__ ((__gnu_inline__)) test5(void)
}
void test_test5() { test5(); }
// PR10233
__inline int test6() { return 0; }
extern int test6();