diff options
Diffstat (limited to 'lib/AST/Decl.cpp')
-rw-r--r-- | lib/AST/Decl.cpp | 94 |
1 files changed, 68 insertions, 26 deletions
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 76d8ec06bf..03cfc83eca 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1883,31 +1883,79 @@ bool FunctionDecl::isInlined() const { return false; } +static bool RedeclForcesDefC99(const FunctionDecl *Redecl) { + // Only consider file-scope declarations in this test. + if (!Redecl->getLexicalDeclContext()->isTranslationUnit()) + return false; + + // Only consider explicit declarations; the presence of a builtin for a + // libcall shouldn't affect whether a definition is externally visible. + if (Redecl->isImplicit()) + return false; + + if (!Redecl->isInlineSpecified() || Redecl->getStorageClass() == SC_Extern) + return true; // Not an inline definition + + 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. +/// Specifically, this determines if adding the current declaration to the set +/// of redeclarations of the given functions causes +/// isInlineDefinitionExternallyVisible to change from false to true. 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) + if (Context.getLangOptions().GNUInline || hasAttr<GNUInlineAttr>()) { + // With GNU inlining, a declaration with 'inline' but not 'extern', forces + // an externally visible definition. + // + // FIXME: What happens if gnu_inline gets added on after the first + // declaration? + if (!isInlineSpecified() || getStorageClassAsWritten() == SC_Extern) + return false; + + const FunctionDecl *Prev = this; + bool FoundBody = false; + while ((Prev = Prev->getPreviousDecl())) { + FoundBody |= Prev->Body; + + if (Prev->Body) { + // If it's not the case that both 'inline' and 'extern' are + // specified on the definition, then it is always externally visible. + if (!Prev->isInlineSpecified() || + Prev->getStorageClassAsWritten() != SC_Extern) + return false; + } else if (Prev->isInlineSpecified() && + Prev->getStorageClassAsWritten() != SC_Extern) { + return false; + } + } + return FoundBody; + } + + if (Context.getLangOptions().CPlusPlus) return false; - if (getLinkage() != ExternalLinkage || isInlineSpecified()) + + // C99 6.7.4p6: + // [...] 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. + if (isInlineSpecified() && getStorageClass() != SC_Extern) return false; - const FunctionDecl *Definition = 0; - if (hasBody(Definition)) - return Definition->isInlined() && - Definition->isInlineDefinitionExternallyVisible(); - return false; + const FunctionDecl *Prev = this; + bool FoundBody = false; + while ((Prev = Prev->getPreviousDecl())) { + FoundBody |= Prev->Body; + if (RedeclForcesDefC99(Prev)) + return false; + } + return FoundBody; } /// \brief For an inline function definition in C or C++, determine whether the @@ -1933,6 +1981,9 @@ bool FunctionDecl::isInlineDefinitionExternallyVisible() const { ASTContext &Context = getASTContext(); if (Context.getLangOptions().GNUInline || hasAttr<GNUInlineAttr>()) { + // Note: If you change the logic here, please change + // doesDeclarationForceExternallyVisibleDefinition as well. + // // If it's not the case that both 'inline' and 'extern' are // specified on the definition, then this inline definition is // externally visible. @@ -1951,7 +2002,7 @@ bool FunctionDecl::isInlineDefinitionExternallyVisible() const { return false; } - + // C99 6.7.4p6: // [...] If all of the file scope declarations for a function in a // translation unit include the inline function specifier without extern, @@ -1959,17 +2010,8 @@ bool FunctionDecl::isInlineDefinitionExternallyVisible() const { for (redecl_iterator Redecl = redecls_begin(), RedeclEnd = redecls_end(); Redecl != RedeclEnd; ++Redecl) { - // Only consider file-scope declarations in this test. - if (!Redecl->getLexicalDeclContext()->isTranslationUnit()) - continue; - - // Only consider explicit declarations; the presence of a builtin for a - // libcall shouldn't affect whether a definition is externally visible. - if (Redecl->isImplicit()) - continue; - - if (!Redecl->isInlineSpecified() || Redecl->getStorageClass() == SC_Extern) - return true; // Not an inline definition + if (RedeclForcesDefC99(*Redecl)) + return true; } // C99 6.7.4p6: |