Skip to content

Commit 47e4093

Browse files
committed
Make a first pass at implementing -Wglobal-constructors. I'm worried that this
will end up bizarrely mirroring CGExprConstant, but that might be the hazard of this feature. llvm-svn: 109984
1 parent 3d4af4e commit 47e4093

File tree

5 files changed

+114
-1
lines changed

5 files changed

+114
-1
lines changed

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def CXXHexFloats : DiagGroup<"c++-hex-floats">;
5252
def : DiagGroup<"c++0x-compat", [CXXHexFloats]>;
5353
def : DiagGroup<"effc++">;
5454
def FourByteMultiChar : DiagGroup<"four-char-constants">;
55+
def GlobalConstructors : DiagGroup<"global-constructors">;
5556
def : DiagGroup<"idiomatic-parentheses">;
5657
def IgnoredQualifiers : DiagGroup<"ignored-qualifiers">;
5758
def : DiagGroup<"import">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,13 @@ def warn_access_decl_deprecated : Warning<
168168
"access declarations are deprecated; use using declarations instead">,
169169
InGroup<Deprecated>;
170170

171+
def warn_global_constructor : Warning<
172+
"declaration requires a global constructor">,
173+
InGroup<GlobalConstructors>, DefaultIgnore;
174+
def warn_global_destructor : Warning<
175+
"declaration requires a global destructor">,
176+
InGroup<GlobalConstructors>, DefaultIgnore;
177+
171178
def err_invalid_thread : Error<
172179
"'__thread' is only allowed on variable declarations">;
173180
def err_thread_non_global : Error<

clang/lib/Sema/SemaDecl.cpp

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3866,6 +3866,57 @@ void Sema::AddInitializerToDecl(DeclPtrTy dcl, ExprArg init) {
38663866
AddInitializerToDecl(dcl, move(init), /*DirectInit=*/false);
38673867
}
38683868

3869+
/// Make a reasonable guess at whether the given initializer will
3870+
/// require a global constructor.
3871+
static bool RequiresGlobalConstructor(Sema &S, Expr *Init) {
3872+
// FIXME: reproducing the logic of CGExprConstant is kindof dumb.
3873+
// Maybe this should be integrated into the constant-evaluator?
3874+
// We'd need array and struct value types.
3875+
//
3876+
// It's probably okay to still warn in the theoretical cases where
3877+
// IR gen can eliminate a global constructor based on
3878+
// initialization order (not that it actually does that
3879+
// optimization at the moment).
3880+
if (Init->isEvaluatable(S.Context)) return false;
3881+
3882+
Init = Init->IgnoreParenNoopCasts(S.Context);
3883+
3884+
// Look through reference-bindings.
3885+
if (CXXBindReferenceExpr *BE = dyn_cast<CXXBindReferenceExpr>(Init))
3886+
return RequiresGlobalConstructor(S, BE);
3887+
3888+
// A constructor call needs a global constructor if:
3889+
if (CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(Init)) {
3890+
// - the constructor is non-trivial
3891+
if (!CE->getConstructor()->isTrivial()) return true;
3892+
3893+
// - any of the argument expressions needs a global constructor
3894+
for (CXXConstructExpr::arg_iterator
3895+
I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I)
3896+
if (RequiresGlobalConstructor(S, *I))
3897+
return true;
3898+
3899+
// We don't have to worry about building temporaries with
3900+
// non-trivial destructors because we should never have walked
3901+
// through the CXXExprWithTemporaries.
3902+
3903+
// So it should be emitted as a constant expression.
3904+
return false;
3905+
}
3906+
3907+
/// An initializer list requires a global constructor if any of the
3908+
/// components do.
3909+
if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) {
3910+
for (unsigned I = 0, E = ILE->getNumInits(); I != E; ++I)
3911+
if (RequiresGlobalConstructor(S, ILE->getInit(I)))
3912+
return true;
3913+
return false;
3914+
}
3915+
3916+
// Assume everything else does.
3917+
return true;
3918+
}
3919+
38693920
/// AddInitializerToDecl - Adds the initializer Init to the
38703921
/// declaration dcl. If DirectInit is true, this is C++ direct
38713922
/// initialization rather than copy initialization.
@@ -4065,6 +4116,11 @@ void Sema::AddInitializerToDecl(DeclPtrTy dcl, ExprArg init, bool DirectInit) {
40654116
VDecl->setInit(Init);
40664117

40674118
if (getLangOptions().CPlusPlus) {
4119+
if (!VDecl->isInvalidDecl() &&
4120+
!VDecl->getDeclContext()->isDependentContext() &&
4121+
VDecl->hasGlobalStorage() && RequiresGlobalConstructor(*this, Init))
4122+
Diag(VDecl->getLocation(), diag::warn_global_constructor);
4123+
40684124
// Make sure we mark the destructor as used if necessary.
40694125
QualType InitType = VDecl->getType();
40704126
while (const ArrayType *Array = Context.getAsArrayType(InitType))
@@ -4270,8 +4326,15 @@ void Sema::ActOnUninitializedDecl(DeclPtrTy dcl,
42704326
MultiExprArg(*this, 0, 0));
42714327
if (Init.isInvalid())
42724328
Var->setInvalidDecl();
4273-
else if (Init.get())
4329+
else if (Init.get()) {
42744330
Var->setInit(MaybeCreateCXXExprWithTemporaries(Init.takeAs<Expr>()));
4331+
4332+
if (getLangOptions().CPlusPlus && !Var->isInvalidDecl() &&
4333+
Var->hasGlobalStorage() &&
4334+
!Var->getDeclContext()->isDependentContext() &&
4335+
RequiresGlobalConstructor(*this, Var->getInit()))
4336+
Diag(Var->getLocation(), diag::warn_global_constructor);
4337+
}
42754338
}
42764339

42774340
if (!Var->isInvalidDecl() && getLangOptions().CPlusPlus && Record)

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5358,6 +5358,9 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) {
53585358
PDiag(diag::err_access_dtor_var)
53595359
<< VD->getDeclName()
53605360
<< VD->getType());
5361+
5362+
if (!VD->isInvalidDecl() && VD->hasGlobalStorage())
5363+
Diag(VD->getLocation(), diag::warn_global_destructor);
53615364
}
53625365
}
53635366

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %clang_cc1 -fsyntax-only -Wglobal-constructors %s -verify
2+
3+
int opaque_int();
4+
5+
namespace test0 {
6+
// These should never require global constructors.
7+
int a;
8+
int b = 20;
9+
float c = 5.0f;
10+
11+
// This global constructor is avoidable based on initialization order.
12+
int d = b; // expected-warning {{global constructor}}
13+
14+
// These global constructors are unavoidable.
15+
int e = opaque_int(); // expected-warning {{global constructor}}
16+
int f = b; // expected-warning {{global constructor}}
17+
}
18+
19+
namespace test1 {
20+
struct A { int x; };
21+
A a;
22+
A b = A();
23+
A c = { 10 };
24+
A d = { opaque_int() }; // expected-warning {{global constructor}}
25+
}
26+
27+
namespace test2 {
28+
struct A { A(); };
29+
A a; // expected-warning {{global constructor}}
30+
A b[10]; // expected-warning {{global constructor}}
31+
A c[10][10]; // expected-warning {{global constructor}}
32+
}
33+
34+
namespace test3 {
35+
struct A { ~A(); };
36+
A a; // expected-warning {{global destructor}}
37+
A b[10]; // expected-warning {{global destructor}}
38+
A c[10][10]; // expected-warning {{global destructor}}
39+
}

0 commit comments

Comments
 (0)