This adds a new warning, -Wglobal-constructors, that warns whenever a decl requires a global constructor or destructor. Global destructors are required whenever a decl with thread_local or global storage is declared with a type with a nontrivial destructor. Global constructors are required whenever a declaration's initializer is a non-trivial, non-constant initializtion.
This warning mirrors the Clang option -Wglobal-constructors, which warns on the same thing. -Wglobal-constructors was present in Apple's GCC and later made its way into Clang. Bootstrapped and regression-tested on x86-64 linux, new tests passing. gcc/ChangeLog: 2019-05-28 Sean Gillespie <s...@swgillespie.me> PR c++/71482 * doc/invoke.texi: Add new flag -Wglobal-constructors. gcc/c-family/ChangeLog: 2019-05-28 Sean Gillespie <s...@swgillespie.me> PR c++/71482 * c.opt: Add new flag -Wglobal-constructors. gcc/cp/ChangeLog: 2019-05-28 Sean Gillespie <s...@swgillespie.me> PR c++/71482 * decl.c (expand_static_init): Warn if a thread local or static decl requires a non-trivial constructor or destructor. gcc/testsuite/ChangeLog: 2019-05-28 Sean Gillespie <s...@swgillespie.me> PR c++/71482 * g++.dg/warn/global-constructors-1.C: New test. * g++.dg/warn/global-constructors-2.C: New test. --- gcc/c-family/c.opt | 4 ++ gcc/cp/decl.c | 22 ++++++-- gcc/doc/invoke.texi | 35 ++++++++++++ .../g++.dg/warn/global-constructors-1.C | 53 +++++++++++++++++++ .../g++.dg/warn/global-constructors-2.C | 49 +++++++++++++++++ 5 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/g++.dg/warn/global-constructors-1.C create mode 100644 gcc/testsuite/g++.dg/warn/global-constructors-2.C diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 046d489f7eb..21f12d4f7b2 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -613,6 +613,10 @@ Wformat-truncation= C ObjC C++ LTO ObjC++ Joined RejectNegative UInteger Var(warn_format_trunc) Warning LangEnabledBy(C ObjC C++ LTO ObjC++,Wformat=, warn_format >= 1, 0) IntegerRange(0, 2) Warn about calls to snprintf and similar functions that truncate output. +Wglobal-constructors +C++ ObjC++ Var(warn_global_constructors) Warning +Warn about objects with static storage duration that require dynamic initialization or have nontrivial destructors. + Wif-not-aligned C ObjC C++ ObjC++ Var(warn_if_not_aligned) Init(1) Warning Warn when the field in a struct is not aligned. diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 19d14a6a5e9..8dc366aa0c6 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -8324,10 +8324,10 @@ expand_static_init (tree decl, tree init) return; } + location_t dloc = DECL_SOURCE_LOCATION (decl); if (CP_DECL_THREAD_LOCAL_P (decl) && DECL_GNU_TLS_P (decl) && !DECL_FUNCTION_SCOPE_P (decl)) { - location_t dloc = DECL_SOURCE_LOCATION (decl); if (init) error_at (dloc, "non-local variable %qD declared %<__thread%> " "needs dynamic initialization", decl); @@ -8467,10 +8467,24 @@ expand_static_init (tree decl, tree init) finish_then_clause (if_stmt); finish_if_stmt (if_stmt); } - else if (CP_DECL_THREAD_LOCAL_P (decl)) - tls_aggregates = tree_cons (init, decl, tls_aggregates); else - static_aggregates = tree_cons (init, decl, static_aggregates); + { + if (CP_DECL_THREAD_LOCAL_P (decl)) + tls_aggregates = tree_cons (init, decl, tls_aggregates); + else + static_aggregates = tree_cons (init, decl, static_aggregates); + + if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (decl))) + { + warning_at (dloc, OPT_Wglobal_constructors, + "declaration requires a global destructor"); + return; + } + + if (DECL_NONTRIVIALLY_INITIALIZED_P (decl) && !decl_constant_var_p (decl)) + warning_at (dloc, OPT_Wglobal_constructors, + "declaration requires a global constructor"); + } } diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 4964cc41ba3..77d324584ec 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -312,6 +312,7 @@ Objective-C and Objective-C++ Dialects}. -Wformat-security -Wformat-signedness -Wformat-truncation=@var{n} @gol -Wformat-y2k -Wframe-address @gol -Wframe-larger-than=@var{byte-size} -Wno-free-nonheap-object @gol +-Wglobal-constructors @gol -Wjump-misses-init @gol -Whsa -Wif-not-aligned @gol -Wignored-qualifiers -Wignored-attributes -Wincompatible-pointer-types @gol @@ -6509,6 +6510,40 @@ to @option{-Wframe-larger-than=}@samp{SIZE_MAX} or larger. Do not warn when attempting to free an object that was not allocated on the heap. +@item -Wglobal-constructors @r{(C++ and Objective-C++ only)} +@opindex Wglobal-constructors +@opindex Wno-global-constructors +Warn whenever an object with static storage duration either requires dynamic +initialization or has a nontrivial destructor. The compiler will issue a +warning if a declaration's initializer is not a constant expression, as shown +in the following examples: + +@smallexample +@group +const char* const tmp = getenv ("TMP"); +// warning: declaration requires a global constructor + +static int initialize() { return 42; } +static int global = initialize (); +// warning: declaration requires a global constructor + +@end group +@end smallexample + +In cases where the compiler can treat the initializer as a constant expression, +no warning is raised, as in the following example: + +@smallexample +@group + +constexpr int initialize() { return 42; } +static int i = initialize (); +// no warning: initialize in this case is a constant expression + +@end group +@end smallexample + + @item -Wstack-usage=@var{byte-size} @opindex Wstack-usage @opindex Wno-stack-usage diff --git a/gcc/testsuite/g++.dg/warn/global-constructors-1.C b/gcc/testsuite/g++.dg/warn/global-constructors-1.C new file mode 100644 index 00000000000..eb08379b680 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/global-constructors-1.C @@ -0,0 +1,53 @@ +// { dg-do compile } +// { dg-options "-Wglobal-constructors" } + +struct with_ctor { + int x; + with_ctor() : x(42) {} +}; + +struct with_dtor { + ~with_dtor() {} +}; + +struct with_both { + int x; + with_both() : x(42) {} + ~with_both() {} +}; + +struct with_default_ctor { + int x; + with_default_ctor() = default; +}; + +struct with_constexpr_ctor { + int x; + constexpr with_constexpr_ctor() : x(42) {} + constexpr with_constexpr_ctor(int x) : x(x) {} +}; + +with_ctor global_var; /* { dg-warning "declaration requires a global constructor" } */ +with_dtor global_var2; /* { dg-warning "declaration requires a global destructor" } */ +with_both global_var3; /* { dg-warning "declaration requires a global destructor" } */ +with_default_ctor global_var4; /* { dg-bogus "declaration requires a global constructor" } */ +with_constexpr_ctor global_var5; /* { dg-bogus "declaration requires a global constructor" } */ + +int initialize() { + return 42; +} + +int global_var6 = initialize(); /* { dg-warning "declaration requires a global constructor" } */ +with_constexpr_ctor global_var7(initialize()); /* { dg-warning "declaration requires a global constructor" } */ + +constexpr int initialize_const() { + return 42; +} + +int global_var8 = initialize_const(); /* { dg-bogus "declaration requires a global constructor" } */ +constexpr int global_var9 = initialize_const(); /* { dg-bogus "declaration requires a global constructor" } */ + + +int main() { + static with_ctor global_var; /* { dg-bogus "declaration requires a global constructor" } */ +} diff --git a/gcc/testsuite/g++.dg/warn/global-constructors-2.C b/gcc/testsuite/g++.dg/warn/global-constructors-2.C new file mode 100644 index 00000000000..e96da348458 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/global-constructors-2.C @@ -0,0 +1,49 @@ +// { dg-do compile } +// { dg-require-effective-target c++11 } +// { dg-options "-Wglobal-constructors" } + +struct with_ctor { + int x; + with_ctor() : x(42) {} +}; + +struct with_dtor { + ~with_dtor() {} +}; + +struct with_both { + int x; + with_both() : x(42) {} + ~with_both() {} +}; + +struct with_default_ctor { + int x; + with_default_ctor() = default; +}; + +struct with_constexpr_ctor { + int x; + constexpr with_constexpr_ctor() : x(42) {} + constexpr with_constexpr_ctor(int x) : x(x) {} +}; + +thread_local with_ctor global_var; /* { dg-warning "declaration requires a global constructor" } */ +thread_local with_dtor global_var2; /* { dg-warning "declaration requires a global destructor" } */ +thread_local with_both global_var3; /* { dg-warning "declaration requires a global destructor" } */ +thread_local with_default_ctor global_var4; /* { dg-bogus "declaration requires a global constructor" } */ +thread_local with_constexpr_ctor global_var5; /* { dg-bogus "declaration requires a global constructor" } */ + +int initialize() { + return 42; +} + +thread_local int global_var6 = initialize(); /* { dg-warning "declaration requires a global constructor" } */ +thread_local with_constexpr_ctor global_var7(initialize()); /* { dg-warning "declaration requires a global constructor" } */ + +constexpr int initialize_const() { + return 42; +} + +thread_local int global_var8 = initialize_const(); /* { dg-bogus "declaration requires a global constructor" } */ +thread_local constexpr int global_var9 = initialize_const(); /* { dg-bogus "declaration requires a global constructor" } */ -- 2.20.1