On 5/28/19 3:30 PM, Sean Gillespie wrote:
This adds a new warning, -Wglobal-constructors, that warns whenever a
decl requires a global constructor or destructor. This new warning fires
whenever a thread_local or global variable is declared whose type has a
non-trivial constructor or destructor. When faced with both a constructor
and a destructor, the error message mentions the destructor and is only
fired once.

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.

I can't tell from the Clang online manual:

Is the warning meant to trigger just for globals, or for all
objects with static storage duration regardless of scope (i.e.,
including namespace-scope objects and static locals and static
class members)?

"Requires a constructor to initialize" doesn't seem very clear
to me.  Is the warning intended to trigger for objects that
require dynamic initialization?  If so, then what about dynamic
intialization of objects of trivial types, such as this:

  static int i = std::string ("foo").length ();

or even

  static int j = strlen (getenv ("TMP"));

If these aren't meant to be diagnosed then the description should
make it clear (the first one involves a ctor, the second one does
not).  But I would think that if the goal is to find sources of
dynamic initialization then diagnosing the above would be useful.
If so, the description should make it clear and tests verifying
that it works should be added.

Martin

PS Dynamic initialization can be optimized into static
initialization, even when it involves a user-defined constructor.
If the purpose of the warning is to find objects that are
dynamically initialized in the sense of the C++ language then
implementing it in the front-end is sufficient.  But if the goal
is to detect constructors that will actually run at runtime (i.e.,
have a startup cost and may have dependencies on one another) then
it needs to be implemented much later.


gcc/ChangeLog:

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

        * doc/invoke.texi: Add new flag -Wglobal-constructors.

gcc/c-family/ChangeLog:

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

        * c.opt: Add new flag -Wglobal-constructors.

gcc/cp/ChangeLog:

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

        * 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>

        * 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                                 | 17 ++++++++++---
  gcc/doc/invoke.texi                           |  7 ++++++
  .../g++.dg/warn/global-constructors-1.C       | 25 +++++++++++++++++++
  .../g++.dg/warn/global-constructors-2.C       | 23 +++++++++++++++++
  5 files changed, 73 insertions(+), 3 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..cf62625ca42 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 when a global declaration requires a constructor to initialize.
+
  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..c1d66195bd7 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -8467,10 +8467,21 @@ 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 (warn_global_constructors)
+     {
+      if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
+        warning(OPT_Wglobal_constructors, "declaration requires a global 
destructor");
+      else
+        warning(OPT_Wglobal_constructors, "declaration requires a global 
constructor");
+     }
+   }
  }
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 4964cc41ba3..a9b2e47a302 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,12 @@ 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 if the compiler detects a global declaration that requires
+a constructor to initialize.
+
  @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..5a6869e9dcd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/global-constructors-1.C
@@ -0,0 +1,25 @@
+// { 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() {}
+};
+
+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" } */
+
+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..e753b9cb507
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/global-constructors-2.C
@@ -0,0 +1,23 @@
+// { 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() {}
+};
+
+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" } */


Reply via email to