diff mbox series

[v2] PR71482: Add -Wglobal-constructors

Message ID 20190601180655.4671-1-sean@swgillespie.me
State New
Headers show
Series [v2] PR71482: Add -Wglobal-constructors | expand

Commit Message

Sean Gillespie June 1, 2019, 6:07 p.m. UTC
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  <sean@swgillespie.me>

	PR c++/71482
	* doc/invoke.texi: Add new flag -Wglobal-constructors.

gcc/c-family/ChangeLog:

2019-05-28  Sean Gillespie  <sean@swgillespie.me>

	PR c++/71482
	* c.opt: Add new flag -Wglobal-constructors.

gcc/cp/ChangeLog:

2019-05-28  Sean Gillespie  <sean@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  <sean@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 mbox series

Patch

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" } */