[PATCH v2] PR71482: Add -Wglobal-constructors

2019-06-01 Thread Sean Gillespie
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  

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

gcc/c-family/ChangeLog:

2019-05-28  Sean Gillespie  

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

gcc/cp/ChangeLog:

2019-05-28  Sean Gillespie  

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  

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
+War

Re: [PATCH] PR71482: Add -Wglobal-constructors

2019-05-31 Thread Sean Gillespie
On Fri, May 31, 2019 at 9:30 AM Martin Sebor  wrote:
>
> On 5/31/19 10:12 AM, Sean Gillespie wrote:
> > On Thu, May 30, 2019 at 4:28 PM Martin Sebor  wrote:
> >>
> >> 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
> >
> > The answer to both of those is yes. Clang warns on both of these with
> > -Wglobal-constructors because the goal of -Wglobal-constructors is to
> > catch and warn against potential life-before-main behavior.
> >
> > Based on Marek's feedback I'm about to push another patch that also
> > warns on your above two examples and adds tests for them. The idea
> > is that any C++-level dynamically initialized globals get warned. Constexpr
> > constructors or things that otherwise can be statically initialized
> > are not warned.
>
> That makes sense.
>
> >
> > Regarding the doc description, how about something like this:
> >
> > "Warns whenever an object with static storage duration requires
> > dynamic initialiation,
> > through global constructors or otherwise."
>
> This makes it clearer -- i.e, global constructors refers to the ELF
> concept rather than to the C++ one.  What does the otherwise part
> refer to?  Some non-ELF mechanism?

Ah, I was thinking "otherwise" to mean the C++ sense of constructor,
and not the ELF one. I realize now that it's clearer without "for
otherwise", since then "global constructors" would refer to just the
ELF concept.

>
> >> 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.
> >>
> >
> > Clang sticks to the C++ language definition of dynamic initialization for 
> > the
> > purposes of this warning and I think we should as well. Without runtime 
> > checks
> > this is a best-effort warning, but it is capable of catching a bunch
> > of the common
> > ways that you can get constructors running on startup, which is the goal.
>
> It might be worth mentioning that in the manual, perhaps along with
> an example showing an instance of the warning that will most likely
> be a true positive vs a false positive.  E.g., something like this:
>
>const char* const tmp = getenv ("TMP");
>
> vs
>
>const int n = strlen ("123");   // initialized statically
>

Will do.

Sean


Re: [PATCH] PR71482: Add -Wglobal-constructors

2019-05-31 Thread Sean Gillespie
On Thu, May 30, 2019 at 4:28 PM Martin Sebor  wrote:
>
> 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

The answer to both of those is yes. Clang warns on both of these with
-Wglobal-constructors because the goal of -Wglobal-constructors is to
catch and warn against potential life-before-main behavior.

Based on Marek's feedback I'm about to push another patch that also
warns on your above two examples and adds tests for them. The idea
is that any C++-level dynamically initialized globals get warned. Constexpr
constructors or things that otherwise can be statically initialized
are not warned.

Regarding the doc description, how about something like this:

"Warns whenever an object with static storage duration requires
dynamic initialiation,
through global constructors or otherwise."

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

Clang sticks to the C++ language definition of dynamic initialization for the
purposes of this warning and I think we should as well. Without runtime checks
this is a best-effort warning, but it is capable of catching a bunch
of the common
ways that you can get constructors running on startup, which is the goal.

Sean Gillespie


Re: [PATCH] PR71482: Add -Wglobal-constructors

2019-05-30 Thread Sean Gillespie
Thank you for the review!

On Thu, May 30, 2019 at 12:38 PM Marek Polacek  wrote:
>
> Thanks for the patch!  Do you have a copyright assignment on file?  Unsure
> if this patch is small enough not to need it.
>

Also unsure, though I assumed that this patch was small enough to not need one.
I'll go ahead and do it regardless.

> > gcc/ChangeLog:
> >
> > 2019-05-28  Sean Gillespie  
> >
> >   * doc/invoke.texi: Add new flag -Wglobal-constructors.
> >
> > gcc/c-family/ChangeLog:
> >
> > 2019-05-28  Sean Gillespie  
> >
> >   * c.opt: Add new flag -Wglobal-constructors.
> >
> > gcc/cp/ChangeLog:
> >
> > 2019-05-28  Sean Gillespie  
> >
> >   * 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  
> >
> >   * g++.dg/warn/global-constructors-1.C: New test.
> >   * g++.dg/warn/global-constructors-2.C: New test.
>
> These are fine but please also mention "PR c++/71482" in them.
>

Will fix.

> > ---
> >  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.
> > +
>
> Ok, I agree this shouldn't be turned on by -Wall or -Wextra.

Yeah - the way I use this with clang is to forbid global constructors
with -Werror=global-constructors and -Wglobal-constructors. Since
this is a legitimate language feature I don't think it should be
included in either of those catch-all warning sets.

>
> > 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)
>
> This check is not wrong but you can probably drop it.  We use it if the 
> warning
> is expensive, but this doesn't seem to be the case.
>

Will fix.

> > + {
> > +  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");
> > + }
> > +   }
>
> The formatting a bit wrong, please use two spaces to indent.  There should be
> a space before a (.  And the lines should not exceed 80 characters.
>
> I think it would be better to use warning_at instead of warning.  As the
> location, you can use 'dloc' defined earlier in the function if you move
> it out of the block.
>

Will fix.

>
> It seems this patch will also warn for
>
> struct A {
>   A() = default;
> };
>
> A a;
>
> which I think we don't want?
>
> I also think you want to add a test using a constexpr constructor.
>
> Marek

You're completely right. I have a modified version of this patch that
expands the test coverage to include these cases while also
handling the general problem of constant and non-constant
initializers. I'll submit it shortly.


Sean Gillespie


[PATCH] PR71482: Add -Wglobal-constructors

2019-05-28 Thread Sean Gillespie
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.

gcc/ChangeLog:

2019-05-28  Sean Gillespie  

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

gcc/c-family/ChangeLog:

2019-05-28  Sean Gillespie  

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

gcc/cp/ChangeLog:

2019-05-28  Sean Gillespie  

* 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  

* 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 000..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-warni