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
This commit is contained in:
John McCall 2013-04-02 02:48:58 +00:00
Родитель 658a28479d
Коммит b421d926cd
6 изменённых файлов: 79 добавлений и 7 удалений

Просмотреть файл

@ -218,6 +218,7 @@ def SizeofArrayArgument : DiagGroup<"sizeof-array-argument">;
def SizeofArrayDecay : DiagGroup<"sizeof-array-decay">;
def SizeofPointerMemaccess : DiagGroup<"sizeof-pointer-memaccess">;
def StaticInInline : DiagGroup<"static-in-inline">;
def StaticLocalInInline : DiagGroup<"static-local-in-inline">;
def GNUStaticFloatInit : DiagGroup<"gnu-static-float-init">;
def StaticFloatInit : DiagGroup<"static-float-init", [GNUStaticFloatInit]>;
def StringPlusInt : DiagGroup<"string-plus-int">;

Просмотреть файл

@ -3328,6 +3328,9 @@ def warn_internal_in_extern_inline : ExtWarn<
def ext_internal_in_extern_inline : Extension<
"static %select{function|variable}0 %1 is used in an inline function with "
"external linkage">, InGroup<StaticInInline>;
def warn_static_local_in_extern_inline : Warning<
"non-constant static local variable in inline function may be different "
"in different files">, InGroup<StaticLocalInInline>;
def note_convert_inline_to_static : Note<
"use 'static' to give inline function %0 internal linkage">;
def note_internal_decl_declared_here : Note<

Просмотреть файл

@ -1375,6 +1375,7 @@ public:
// Returns true if the variable declaration is a redeclaration
bool CheckVariableDeclaration(VarDecl *NewVD, LookupResult &Previous);
void CheckCompleteVariableDeclaration(VarDecl *var);
void MaybeSuggestAddingStaticToDecl(const FunctionDecl *D);
void ActOnStartFunctionDeclarator();
void ActOnEndFunctionDeclarator();
NamedDecl* ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,

Просмотреть файл

@ -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)

Просмотреть файл

@ -214,19 +214,24 @@ static void diagnoseUseOfInternalDeclInInlineFunction(Sema &S,
: diag::warn_internal_in_extern_inline)
<< /*IsVar=*/!UsedFn << D;
// Suggest "static" on the inline function, if possible.
if (!hasAnyExplicitStorageClass(Current)) {
const FunctionDecl *FirstDecl = Current->getCanonicalDecl();
SourceLocation DeclBegin = FirstDecl->getSourceRange().getBegin();
S.Diag(DeclBegin, diag::note_convert_inline_to_static)
<< Current << FixItHint::CreateInsertion(DeclBegin, "static ");
}
S.MaybeSuggestAddingStaticToDecl(Current);
S.Diag(D->getCanonicalDecl()->getLocation(),
diag::note_internal_decl_declared_here)
<< D;
}
void Sema::MaybeSuggestAddingStaticToDecl(const FunctionDecl *Cur) {
const FunctionDecl *First = Cur->getFirstDeclaration();
// Suggest "static" on the function, if possible.
if (!hasAnyExplicitStorageClass(First)) {
SourceLocation DeclBegin = First->getSourceRange().getBegin();
Diag(DeclBegin, diag::note_convert_inline_to_static)
<< Cur << FixItHint::CreateInsertion(DeclBegin, "static ");
}
}
/// \brief Determine whether the use of this declaration is valid, and
/// emit any corresponding diagnostics.
///

Просмотреть файл

@ -73,6 +73,16 @@ inline int useStaticAgain () { // expected-note 2 {{use 'static' to give inline
#pragma clang diagnostic pop
inline void defineStaticVar() { // expected-note {{use 'static' to give inline function 'defineStaticVar' internal linkage}}
static const int x = 0; // ok
static int y = 0; // expected-warning {{non-constant static local variable in inline function may be different in different files}}
}
extern inline void defineStaticVarInExtern() {
static const int x = 0; // ok
static int y = 0; // ok
}
#endif