diff options
-rw-r--r-- | include/clang/Basic/DiagnosticGroups.td | 1 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 3 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 1 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 52 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 19 | ||||
-rw-r--r-- | test/Sema/inline.c | 10 |
6 files changed, 79 insertions, 7 deletions
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 3114a1fb22..a12a4f974e 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -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">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 4bb4fba853..858df1754b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index a8f18fd571..7b07352d3c 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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, 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) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 75da99c344..c1d86550a5 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -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. /// diff --git a/test/Sema/inline.c b/test/Sema/inline.c index c27c00efaa..496e282eca 100644 --- a/test/Sema/inline.c +++ b/test/Sema/inline.c @@ -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 |