diff options
author | John McCall <rjmccall@apple.com> | 2013-04-02 02:48:58 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2013-04-02 02:48:58 +0000 |
commit | b421d926cdc632489915d39556f04c14f59d2392 (patch) | |
tree | 569f9f4c7324bdc780e0bf6b910830b620994f79 /lib/Sema/SemaDecl.cpp | |
parent | 658a28479dd775f6ff2c07fa5699a7ea01e04127 (diff) |
Add -Wstatic-local-in-inline, which warns about using a static local
variable in a C99 inline (but not static-inline or extern-inline)
function definition.
The standard doesn't actually say that this doesn't apply to
"extern inline" definitions, but that seems like a useful extension,
and it at least doesn't have the obvious flaw that a static
mutable variable in an externally-available definition does.
rdar://13535367
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@178520 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaDecl.cpp')
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 5f7399a3d4..beae217e01 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -4644,6 +4644,38 @@ static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) { } } +/// Given that we are within the definition of the given function, +/// will that definition behave like C99's 'inline', where the +/// definition is discarded except for optimization purposes? +static bool isFunctionDefinitionDiscarded(Sema &S, FunctionDecl *FD) { + // Try to avoid calling GetGVALinkageForFunction. + + // All cases of this require the 'inline' keyword. + if (!FD->isInlined()) return false; + + // This is only possible in C++ with the gnu_inline attribute. + if (S.getLangOpts().CPlusPlus && !FD->hasAttr<GNUInlineAttr>()) + return false; + + // Okay, go ahead and call the relatively-more-expensive function. + +#ifndef NDEBUG + // AST quite reasonably asserts that it's working on a function + // definition. We don't really have a way to tell it that we're + // currently defining the function, so just lie to it in +Asserts + // builds. This is an awful hack. + FD->setLazyBody(1); +#endif + + bool isC99Inline = (S.Context.GetGVALinkageForFunction(FD) == GVA_C99Inline); + +#ifndef NDEBUG + FD->setLazyBody(0); +#endif + + return isC99Inline; +} + static bool shouldConsiderLinkage(const VarDecl *VD) { const DeclContext *DC = VD->getDeclContext()->getRedeclContext(); if (DC->isFunctionOrMethod()) @@ -4693,6 +4725,7 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, D.setInvalidType(); SC = SC_None; } + SCSpec = D.getDeclSpec().getStorageClassSpecAsWritten(); VarDecl::StorageClass SCAsWritten = StorageClassSpecToVarDeclStorageClass(SCSpec); @@ -4865,6 +4898,25 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, NewVD->setThreadSpecified(true); } + // C99 6.7.4p3 + // An inline definition of a function with external linkage shall + // not contain a definition of a modifiable object with static or + // thread storage duration... + // We only apply this when the function is required to be defined + // elsewhere, i.e. when the function is not 'extern inline'. Note + // that a local variable with thread storage duration still has to + // be marked 'static'. Also note that it's possible to get these + // semantics in C++ using __attribute__((gnu_inline)). + if (SC == SC_Static && S->getFnParent() != 0 && + !NewVD->getType().isConstQualified()) { + FunctionDecl *CurFD = getCurFunctionDecl(); + if (CurFD && isFunctionDefinitionDiscarded(*this, CurFD)) { + Diag(D.getDeclSpec().getStorageClassSpecLoc(), + diag::warn_static_local_in_extern_inline); + MaybeSuggestAddingStaticToDecl(CurFD); + } + } + if (D.getDeclSpec().isModulePrivateSpecified()) { if (isExplicitSpecialization) Diag(NewVD->getLocation(), diag::err_module_private_specialization) |