[PATCH v2] c++: Implement -Wuninitialized for mem-initializers (redux) [PR19808]

Marek Polacek polacek@redhat.com
Mon Nov 8 23:41:20 GMT 2021


On Sun, Nov 07, 2021 at 12:45:24AM -0400, Jason Merrill wrote:
> On 11/5/21 19:22, Marek Polacek wrote:
> > 2021 update: Last year I posted a version of this patch:
> > <https://gcc.gnu.org/pipermail/gcc-patches/2020-November/559162.html>
> > but it didn't make it in.  The main objection seemed to be that the
> > patch tried to do too much, and overlapped with the ME uninitialized
> > warnings.  Since the patch used walk_tree without any data flow info,
> > it issued false positives for things like a(0 ? b : 42) and similar.
> > 
> > I'll admit I've been dreading resurrecting this because of the lack
> > of clarity about where we should warn about what.  On the other hand,
> > I think we really should do something about this.  So I've simplified
> > the original patch as much as it seemed reasonable.  For instance, it
> > doesn't even attempt to handle cases like "a((b = 42)), c(b)" -- for
> > these I simply give up for the whole mem-initializer (but who writes
> > code like that, anyway?).  I also give up when a member is initialized
> > with a function call, because we don't know what the call could do.
> > See Wuninitialized-17.C, for which clang emits a false positive but
> > we don't.  I remember having a hard time dealing with initializer lists
> > in my previous patch, so now I only handle simple a{b} cases, but no
> > more.  It turned out that this abridged version still warns about 90%
> > cases where users would expect a warning.
> 
> Sounds good.
> 
> > More complicated cases are left for the ME, which, for unused inline
> > functions, will only warn with -fkeep-inline-functions, but so be it.
> 
> This seems like a broader issue that should really be addressed; since we
> have warnings that depend on running optimization passes, there should be a
> way to force the function through to that point even if we don't need to
> write it out.  This is also the problem with bug 21678.
 
Presumably not addressed as part of this patch ;)  FWIW, it will probably
need a separate option given how expensive that will be...

> > @@ -820,6 +936,12 @@ perform_member_init (tree member, tree init)
> >   	warning_at (DECL_SOURCE_LOCATION (current_function_decl),
> >   		    OPT_Winit_self, "%qD is initialized with itself",
> >   		    member);
> > +      else if (!uninitialized.is_empty ())
> > +	{
> > +	  find_uninit_data data = { &uninitialized, decl };
> > +	  cp_walk_tree_without_duplicates (&val, find_uninit_fields_r,
> > +					   &data);
> > +	}
> 
> You repeat this pattern three times; it might be good to factor it into a
> find_uninit_fields function.

Done.
 
> >       }
> >     if (array_of_unknown_bound_p (type))
> > @@ -848,6 +970,9 @@ perform_member_init (tree member, tree init)
> >   	 do aggregate-initialization.  */
> >       }
> > +  /* Assume we are initializing the member.  */
> > +  bool member_initialized_p = true;
> > +
> >     if (init == void_type_node)
> >       {
> >         /* mem() means value-initialization.  */
> > @@ -988,6 +1113,9 @@ perform_member_init (tree member, tree init)
> >   	    diagnose_uninitialized_cst_or_ref_member (core_type,
> >   						      /*using_new=*/false,
> >   						      /*complain=*/true);
> > +
> > +	  /* We left the member uninitialized.  */
> > +	  member_initialized_p = false;
> >   	}
> >         maybe_warn_list_ctor (member, init);
> > @@ -998,6 +1126,11 @@ perform_member_init (tree member, tree init)
> >   						tf_warning_or_error));
> >       }
> > +  if (member_initialized_p && warn_uninitialized)
> > +    /* This member is now initialized, remove it from the uninitialized
> > +       set.  */
> > +    uninitialized.remove (member);
> > +
> >     if (type_build_dtor_call (type))
> >       {
> >         tree expr;
> > @@ -1311,13 +1444,30 @@ emit_mem_initializers (tree mem_inits)
> >     if (!COMPLETE_TYPE_P (current_class_type))
> >       return;
> > +  /* Keep a set holding fields that are not initialized.  */
> > +  hash_set<tree> uninitialized;
> > +
> > +  /* Initially that is all of them.  */
> > +  if (warn_uninitialized)
> > +    for (tree field = TYPE_FIELDS (current_class_type); field;
> > +	 field = TREE_CHAIN (field))
> > +      if (TREE_CODE (field) == FIELD_DECL && !DECL_ARTIFICIAL (field))
> 
> I think you need to check DECL_UNNAMED_BIT_FIELD here.  Incidentally, so
> does default_init_uninitialized_part:
> 
> struct A
> {
>   int : 8;
>   int i = 42;
> };
> 
> constexpr A a; // bogus error

I sent a patch earlier today for this:
https://gcc.gnu.org/pipermail/gcc-patches/2021-November/583717.html
 
> It would probably be better to use next_initializable_field more widely.
> But you'd still need to check DECL_ARTIFICIAL to exclude the base/vtable
> fields that function includes.

OK, done.

Here's a v2, which also fixes a bug that Martin S. found (in the CALL_EXPR
block).  

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
2021 update: Last year I posted a version of this patch:
<https://gcc.gnu.org/pipermail/gcc-patches/2020-November/559162.html>
but it didn't make it in.  The main objection seemed to be that the
patch tried to do too much, and overlapped with the ME uninitialized
warnings.  Since the patch used walk_tree without any data flow info,
it issued false positives for things like a(0 ? b : 42) and similar.

I'll admit I've been dreading resurrecting this because of the lack
of clarity about where we should warn about what.  On the other hand,
I think we really should do something about this.  So I've simplified
the original patch as much as it seemed reasonable.  For instance, it
doesn't even attempt to handle cases like "a((b = 42)), c(b)" -- for
these I simply give up for the whole mem-initializer (but who writes
code like that, anyway?).  I also give up when a member is initialized
with a function call, because we don't know what the call could do.
See Wuninitialized-17.C, for which clang emits a false positive but
we don't.  I remember having a hard time dealing with initializer lists
in my previous patch, so now I only handle simple a{b} cases, but no
more.  It turned out that this abridged version still warns about 90%
cases where users would expect a warning.

More complicated cases are left for the ME, which, for unused inline
functions, will only warn with -fkeep-inline-functions, but so be it.
(This is bug 21678.)

This patch implements the long-desired -Wuninitialized warning for
member initializer lists, so that the front end can detect bugs like

  struct A {
    int a;
    int b;
    A() : b(1), a(b) { }
  };

where the field 'b' is used uninitialized because the order of member
initializers in the member initializer list is irrelevant; what matters
is the order of declarations in the class definition.

I've implemented this by keeping a hash set holding fields that are not
initialized yet, so at first it will be {a, b}, and after initializing
'a' it will be {b} and so on.  Then I use walk_tree to walk the
initializer and if we see that an uninitialized object is used, we warn.
Of course, when we use the address of the object, we may not warn:

  struct B {
    int &r;
    int *p;
    int a;
    B() : r(a), p(&a), a(1) { } // ok
  };

Likewise, don't warn in unevaluated contexts such as sizeof.  Classes
without an explicit initializer may still be initialized by their
default constructors; whether or not something is considered initialized
is handled in perform_member_init, see member_initialized_p.

	PR c++/19808
	PR c++/96121

gcc/cp/ChangeLog:

	* init.c (perform_member_init): Remove a forward declaration.
	Walk the initializer using find_uninit_fields_r.  New parameter
	to track uninitialized fields.  If a member is initialized,
	remove it from the hash set.
	(perform_target_ctor): Return the initializer.
	(struct find_uninit_data): New class.
	(find_uninit_fields_r): New function.
	(find_uninit_fields): New function.
	(emit_mem_initializers): Keep and initialize a set holding fields
	that are not initialized.  When handling delegating constructors,
	walk the constructor tree using find_uninit_fields_r.  Also when
	initializing base clases.  Pass uninitialized down to
	perform_member_init.

gcc/ChangeLog:

	* doc/invoke.texi: Update documentation for -Wuninitialized.
	* tree.c (stabilize_reference): Set location.

gcc/testsuite/ChangeLog:

	* g++.dg/warn/Wuninitialized-14.C: New test.
	* g++.dg/warn/Wuninitialized-15.C: New test.
	* g++.dg/warn/Wuninitialized-16.C: New test.
	* g++.dg/warn/Wuninitialized-17.C: New test.
	* g++.dg/warn/Wuninitialized-18.C: New test.
	* g++.dg/warn/Wuninitialized-19.C: New test.
	* g++.dg/warn/Wuninitialized-20.C: New test.
	* g++.dg/warn/Wuninitialized-21.C: New test.
	* g++.dg/warn/Wuninitialized-22.C: New test.
	* g++.dg/warn/Wuninitialized-23.C: New test.
	* g++.dg/warn/Wuninitialized-24.C: New test.
	* g++.dg/warn/Wuninitialized-25.C: New test.
	* g++.dg/warn/Wuninitialized-26.C: New test.
	* g++.dg/warn/Wuninitialized-27.C: New test.
	* g++.dg/warn/Wuninitialized-28.C: New test.
	* g++.dg/warn/Wuninitialized-29.C: New test.
	* g++.dg/warn/Wuninitialized-30.C: New test.
---
 gcc/cp/init.c                                 | 194 ++++++++++++++++--
 gcc/doc/invoke.texi                           |  12 ++
 gcc/testsuite/g++.dg/warn/Wuninitialized-14.C |  31 +++
 gcc/testsuite/g++.dg/warn/Wuninitialized-15.C | 118 +++++++++++
 gcc/testsuite/g++.dg/warn/Wuninitialized-16.C |  12 ++
 gcc/testsuite/g++.dg/warn/Wuninitialized-17.C |  33 +++
 gcc/testsuite/g++.dg/warn/Wuninitialized-18.C |  22 ++
 gcc/testsuite/g++.dg/warn/Wuninitialized-19.C |  50 +++++
 gcc/testsuite/g++.dg/warn/Wuninitialized-20.C |  16 ++
 gcc/testsuite/g++.dg/warn/Wuninitialized-21.C |  20 ++
 gcc/testsuite/g++.dg/warn/Wuninitialized-22.C |  37 ++++
 gcc/testsuite/g++.dg/warn/Wuninitialized-23.C |  24 +++
 gcc/testsuite/g++.dg/warn/Wuninitialized-24.C |  89 ++++++++
 gcc/testsuite/g++.dg/warn/Wuninitialized-25.C |  12 ++
 gcc/testsuite/g++.dg/warn/Wuninitialized-26.C |  22 ++
 gcc/testsuite/g++.dg/warn/Wuninitialized-27.C |  20 ++
 gcc/testsuite/g++.dg/warn/Wuninitialized-28.C |  59 ++++++
 gcc/testsuite/g++.dg/warn/Wuninitialized-29.C |  59 ++++++
 gcc/testsuite/g++.dg/warn/Wuninitialized-30.C |  13 ++
 gcc/tree.c                                    |   1 +
 20 files changed, 831 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-14.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-15.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-16.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-17.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-18.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-19.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-20.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-21.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-22.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-23.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-24.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-25.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-26.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-27.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-28.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-29.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wuninitialized-30.C

diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 771a19bc402..b5ae396365f 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -41,7 +41,6 @@ static tree finish_init_stmts (bool, tree, tree);
 static void construct_virtual_base (tree, tree);
 static bool expand_aggr_init_1 (tree, tree, tree, tree, int, tsubst_flags_t);
 static bool expand_default_init (tree, tree, tree, tree, int, tsubst_flags_t);
-static void perform_member_init (tree, tree);
 static int member_init_ok_or_else (tree, tree, tree);
 static void expand_virtual_init (tree, tree);
 static tree sort_mem_initializers (tree, tree);
@@ -525,19 +524,19 @@ build_value_init_noctor (tree type, tsubst_flags_t complain)
   return build_zero_init (type, NULL_TREE, /*static_storage_p=*/false);
 }
 
-/* Initialize current class with INIT, a TREE_LIST of
-   arguments for a target constructor. If TREE_LIST is void_type_node,
-   an empty initializer list was given.  */
+/* Initialize current class with INIT, a TREE_LIST of arguments for
+   a target constructor.  If TREE_LIST is void_type_node, an empty
+   initializer list was given.  Return the target constructor.  */
 
-static void
+static tree
 perform_target_ctor (tree init)
 {
   tree decl = current_class_ref;
   tree type = current_class_type;
 
-  finish_expr_stmt (build_aggr_init (decl, init,
-				     LOOKUP_NORMAL|LOOKUP_DELEGATING_CONS,
-				     tf_warning_or_error));
+  init = build_aggr_init (decl, init, LOOKUP_NORMAL|LOOKUP_DELEGATING_CONS,
+			  tf_warning_or_error);
+  finish_expr_stmt (init);
   if (type_build_dtor_call (type))
     {
       tree expr = build_delete (input_location,
@@ -550,6 +549,7 @@ perform_target_ctor (tree init)
 	  && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
 	finish_eh_cleanup (expr);
     }
+  return init;
 }
 
 /* Return the non-static data initializer for FIELD_DECL MEMBER.  */
@@ -775,12 +775,148 @@ maybe_warn_list_ctor (tree member, tree init)
 	     "of the underlying array", member, begin);
 }
 
+/* Data structure for find_uninit_fields_r, below.  */
+
+struct find_uninit_data {
+  /* The set tracking the yet-uninitialized members.  */
+  hash_set<tree> *uninitialized;
+  /* The data member we are currently initializing.  It can be either
+     a type (initializing a base class/delegating constructors), or
+     a COMPONENT_REF.  */
+  tree member;
+};
+
+/* walk_tree callback that warns about using uninitialized data in
+   a member-initializer-list.  */
+
+static tree
+find_uninit_fields_r (tree *tp, int *walk_subtrees, void *data)
+{
+  find_uninit_data *d = static_cast<find_uninit_data *>(data);
+  hash_set<tree> *uninitialized = d->uninitialized;
+  tree init = *tp;
+  const tree_code code = TREE_CODE (init);
+
+  /* No need to look into types or unevaluated operands.  */
+  if (TYPE_P (init) || unevaluated_p (code))
+    {
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
+
+  switch (code)
+    {
+    /* We'd need data flow info to avoid false positives.  */
+    case COND_EXPR:
+    case VEC_COND_EXPR:
+    case BIND_EXPR:
+    /* We might see a MODIFY_EXPR in cases like S() : a((b = 42)), c(b) { }
+       where the initializer for 'a' surreptitiously initializes 'b'.  Let's
+       not bother with these complicated scenarios in the front end.  */
+    case MODIFY_EXPR:
+    /* Don't attempt to handle statement-expressions, either.  */
+    case STATEMENT_LIST:
+      uninitialized->empty ();
+      gcc_fallthrough ();
+    /* If we're just taking the address of an object, it doesn't matter
+       whether it's been initialized.  */
+    case ADDR_EXPR:
+      *walk_subtrees = false;
+      return NULL_TREE;
+    default:
+      break;
+    }
+
+  /* We'd need data flow info to avoid false positives.  */
+  if (truth_value_p (code))
+    goto give_up;
+  /* Attempt to handle a simple a{b}, but no more.  */
+  else if (BRACE_ENCLOSED_INITIALIZER_P (init))
+    {
+      if (CONSTRUCTOR_NELTS (init) == 1
+	  && !BRACE_ENCLOSED_INITIALIZER_P (CONSTRUCTOR_ELT (init, 0)->value))
+	init = CONSTRUCTOR_ELT (init, 0)->value;
+      else
+	goto give_up;
+    }
+  /* Warn about uninitialized 'this'.  */
+  else if (code == CALL_EXPR)
+    {
+      tree fn = get_callee_fndecl (init);
+      if (fn && DECL_NONSTATIC_MEMBER_FUNCTION_P (fn))
+	{
+	  tree op = CALL_EXPR_ARG (init, 0);
+	  if (TREE_CODE (op) == ADDR_EXPR)
+	    op = TREE_OPERAND (op, 0);
+	  temp_override<tree> ovr (d->member, DECL_ARGUMENTS (fn));
+	  cp_walk_tree_without_duplicates (&op, find_uninit_fields_r, data);
+	}
+      /* Functions (whether static or nonstatic member) may have side effects
+	 and initialize other members; it's not the front end's job to try to
+	 figure it out.  But don't give up for constructors: we still want to
+	 warn when initializing base classes:
+
+	   struct D : public B {
+	     int x;
+	     D() : B(x) {}
+	   };
+
+	 so carry on to detect that 'x' is used uninitialized.  */
+      if (!fn || !DECL_CONSTRUCTOR_P (fn))
+	goto give_up;
+    }
+
+  /* If we find FIELD in the uninitialized set, we warn.  */
+  if (code == COMPONENT_REF)
+    {
+      tree field = TREE_OPERAND (init, 1);
+      tree type = TYPE_P (d->member) ? d->member : TREE_TYPE (d->member);
+
+      /* We're initializing a reference member with itself.  */
+      if (TYPE_REF_P (type) && cp_tree_equal (d->member, init))
+	warning_at (EXPR_LOCATION (init), OPT_Winit_self,
+		    "%qD is initialized with itself", field);
+      else if (cp_tree_equal (TREE_OPERAND (init, 0), current_class_ref)
+	       && uninitialized->contains (field))
+	{
+	  if (TYPE_REF_P (TREE_TYPE (field)))
+	    warning_at (EXPR_LOCATION (init), OPT_Wuninitialized,
+			"reference %qD is not yet bound to a value when used "
+			"here", field);
+	  else if (!INDIRECT_TYPE_P (type) || is_this_parameter (d->member))
+	    warning_at (EXPR_LOCATION (init), OPT_Wuninitialized,
+			"member %qD is used uninitialized", field);
+	  *walk_subtrees = false;
+	}
+    }
+
+  return NULL_TREE;
+
+give_up:
+  *walk_subtrees = false;
+  uninitialized->empty ();
+  return integer_zero_node;
+}
+
+/* Wrapper around find_uninit_fields_r above.  */
+
+static void
+find_uninit_fields (tree *t, hash_set<tree> *uninitialized, tree member)
+{
+  if (!uninitialized->is_empty ())
+    {
+      find_uninit_data data = { uninitialized, member };
+      cp_walk_tree_without_duplicates (t, find_uninit_fields_r, &data);
+    }
+}
+
 /* Initialize MEMBER, a FIELD_DECL, with INIT, a TREE_LIST of
    arguments.  If TREE_LIST is void_type_node, an empty initializer
-   list was given; if NULL_TREE no initializer was given.  */
+   list was given; if NULL_TREE no initializer was given.  UNINITIALIZED
+   is the hash set that tracks uninitialized fields.  */
 
 static void
-perform_member_init (tree member, tree init)
+perform_member_init (tree member, tree init, hash_set<tree> &uninitialized)
 {
   tree decl;
   tree type = TREE_TYPE (member);
@@ -808,7 +944,9 @@ perform_member_init (tree member, tree init)
   if (decl == error_mark_node)
     return;
 
-  if (warn_init_self && init && TREE_CODE (init) == TREE_LIST
+  if ((warn_init_self || warn_uninitialized)
+      && init
+      && TREE_CODE (init) == TREE_LIST
       && TREE_CHAIN (init) == NULL_TREE)
     {
       tree val = TREE_VALUE (init);
@@ -820,6 +958,8 @@ perform_member_init (tree member, tree init)
 	warning_at (DECL_SOURCE_LOCATION (current_function_decl),
 		    OPT_Winit_self, "%qD is initialized with itself",
 		    member);
+      else
+	find_uninit_fields (&val, &uninitialized, decl);
     }
 
   if (array_of_unknown_bound_p (type))
@@ -848,6 +988,9 @@ perform_member_init (tree member, tree init)
 	 do aggregate-initialization.  */
     }
 
+  /* Assume we are initializing the member.  */
+  bool member_initialized_p = true;
+
   if (init == void_type_node)
     {
       /* mem() means value-initialization.  */
@@ -988,6 +1131,9 @@ perform_member_init (tree member, tree init)
 	    diagnose_uninitialized_cst_or_ref_member (core_type,
 						      /*using_new=*/false,
 						      /*complain=*/true);
+
+	  /* We left the member uninitialized.  */
+	  member_initialized_p = false;
 	}
 
       maybe_warn_list_ctor (member, init);
@@ -998,6 +1144,11 @@ perform_member_init (tree member, tree init)
 						tf_warning_or_error));
     }
 
+  if (member_initialized_p && warn_uninitialized)
+    /* This member is now initialized, remove it from the uninitialized
+       set.  */
+    uninitialized.remove (member);
+
   if (type_build_dtor_call (type))
     {
       tree expr;
@@ -1311,13 +1462,25 @@ emit_mem_initializers (tree mem_inits)
   if (!COMPLETE_TYPE_P (current_class_type))
     return;
 
+  /* Keep a set holding fields that are not initialized.  */
+  hash_set<tree> uninitialized;
+
+  /* Initially that is all of them.  */
+  if (warn_uninitialized)
+    for (tree f = next_initializable_field (TYPE_FIELDS (current_class_type));
+	 f != NULL_TREE;
+	 f = next_initializable_field (DECL_CHAIN (f)))
+      if (!DECL_ARTIFICIAL (f))
+	uninitialized.add (f);
+
   if (mem_inits
       && TYPE_P (TREE_PURPOSE (mem_inits))
       && same_type_p (TREE_PURPOSE (mem_inits), current_class_type))
     {
       /* Delegating constructor. */
       gcc_assert (TREE_CHAIN (mem_inits) == NULL_TREE);
-      perform_target_ctor (TREE_VALUE (mem_inits));
+      tree ctor = perform_target_ctor (TREE_VALUE (mem_inits));
+      find_uninit_fields (&ctor, &uninitialized, current_class_type);
       return;
     }
 
@@ -1378,6 +1541,9 @@ emit_mem_initializers (tree mem_inits)
 			      flags,
                               tf_warning_or_error);
 	  expand_cleanup_for_base (subobject, NULL_TREE);
+	  if (STATEMENT_LIST_TAIL (cur_stmt_list))
+	    find_uninit_fields (&STATEMENT_LIST_TAIL (cur_stmt_list)->stmt,
+				&uninitialized, BINFO_TYPE (subobject));
 	}
       else if (!ABSTRACT_CLASS_TYPE_P (current_class_type))
 	/* C++14 DR1658 Means we do not have to construct vbases of
@@ -1405,7 +1571,9 @@ emit_mem_initializers (tree mem_inits)
       iloc_sentinel ils (EXPR_LOCATION (TREE_TYPE (mem_inits)));
 
       perform_member_init (TREE_PURPOSE (mem_inits),
-			   TREE_VALUE (mem_inits));
+			   TREE_VALUE (mem_inits),
+			   uninitialized);
+
       mem_inits = TREE_CHAIN (mem_inits);
     }
 }
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index f206cff1221..67cd8e9eed8 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -6982,6 +6982,18 @@ to compute a value that itself is never used, because such
 computations may be deleted by data flow analysis before the warnings
 are printed.
 
+In C++, this warning also warns about using uninitialized objects in
+member-initializer-lists.  For example, GCC warns about @code{b} being
+uninitialized in the following snippet:
+
+@smallexample
+struct A @{
+  int a;
+  int b;
+  A() : a(b) @{ @}
+@};
+@end smallexample
+
 @item -Wno-invalid-memory-model
 @opindex Winvalid-memory-model
 @opindex Wno-invalid-memory-model
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-14.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-14.C
new file mode 100644
index 00000000000..cebadf1c269
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-14.C
@@ -0,0 +1,31 @@
+// PR c++/19808
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized" }
+
+struct A {
+  int m;
+  int get() const { return m; }
+
+  A() : m{} { }
+  A(int) { }
+  A(const A &) { }
+  A(A *) { }
+};
+
+struct S {
+  A a, b;
+
+  S(int (*)[1]) : a() {}
+  S(int (*)[2]) : b(a.get()) {}
+  S(int (*)[3]) : b(a) {}
+  S(int (*)[4]) : a(&a) {}
+};
+
+struct R {
+  A a, b;
+
+  R(int (*)[1]) : a{} {}
+  R(int (*)[2]) : b{a.get()} {}
+  R(int (*)[3]) : b{a} {}
+  R(int (*)[4]) : a{&a} {}
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-15.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-15.C
new file mode 100644
index 00000000000..89e90668c41
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-15.C
@@ -0,0 +1,118 @@
+// PR c++/19808
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized -Winit-self" }
+// Largely copied from clang's test/SemaCXX/uninitialized.cpp.
+
+int x;
+struct U {
+  U() : b(a) { }
+  int &a = x;
+  int &b;
+};
+
+struct T {
+  T() : a(b), b(a) { } // { dg-warning "reference .T::b. is not yet bound" }
+  int &a, &b;
+};
+
+struct S {
+  S() : a(a) { } // { dg-warning ".S::a. is initialized with itself" }
+  int &a;
+};
+
+struct A {
+  int a;
+  int b;
+  A() { }
+  A(int (*)[1]) : b(a) { } // { dg-warning ".A::a. is used uninitialized" }
+  A(int (*)[2]) : a(b) { } // { dg-warning ".A::b. is used uninitialized" }
+};
+
+struct D {
+  int a;
+  int &b;
+  int &c = a;
+  int d = b;
+  D() : b(a) { }
+};
+
+struct E {
+  int a;
+  int get();
+  static int num();
+  E() { }
+  E(int) { }
+};
+
+struct F {
+  int a;
+  E e;
+  int b;
+  F(int (*)[1]) : a(e.get()) { } // { dg-warning "member .F::e. is used uninitialized" }
+  F(int (*)[2]) : a(e.num()) { }
+  F(int (*)[3]) : e(a) { } // { dg-warning "member .F::a. is used uninitialized" }
+  F(int (*)[4]) : a(4), e(a) { }
+  F(int (*)[5]) : e(b) { } // { dg-warning "member .F::b. is used uninitialized" }
+  F(int (*)[6]) : e(b), b(4) { } // { dg-warning "member .F::b. is used uninitialized" }
+};
+
+struct G {
+  G(const A&) { };
+};
+
+struct H {
+  A a1;
+  G g;
+  A a2;
+  H() : g(a1) { }
+  // ??? clang++ doesn't warn here
+  H(int) : g(a2) { } // { dg-warning "member .H::a2. is used uninitialized" }
+};
+
+struct I {
+  I(int *) { }
+};
+
+struct J : I {
+  int *a;
+  int *b;
+  int c;
+  J() : I((a = new int(5))), b(a), c(*a) { }
+};
+
+struct M { };
+
+struct N : public M {
+  int a;
+  int b;
+  N() : b(a) { } // { dg-warning "member .N::a. is used uninitialized" }
+};
+
+struct O {
+  int x = 42;
+  int get() { return x; }
+};
+
+struct P {
+  O o;
+  int x = o.get();
+  P() : x(o.get()) { }
+};
+
+struct Q {
+  int a;
+  int b;
+  int &c;
+  Q() :
+    a(c = 5), // "reference .Q::c. is not yet bound" but too complex for the FE
+    b(c), // "reference .Q::c. is not yet bound" but too complex for the FE
+    c(a) { }
+};
+
+struct R {
+  int a;
+  int b;
+  int c;
+  int d = a + b + c;
+  R() : a(c = 5), b(c), c(a) { }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-16.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-16.C
new file mode 100644
index 00000000000..38f587bc024
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-16.C
@@ -0,0 +1,12 @@
+// PR c++/19808
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized" }
+
+struct S {
+  int a;
+  int b;
+  int c;
+  S() : a((b = 42)), c(b) { }
+  S(int) : a(((1, b) = 42)), c(b) { }
+  S(char) : a(((c++, b) = 42)), c(b) { } // "field .S::c. is used uninitialized" but too complex for the FE
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-17.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-17.C
new file mode 100644
index 00000000000..80c37aca735
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-17.C
@@ -0,0 +1,33 @@
+// PR c++/19808
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized -Winit-self" }
+
+int
+foo (int *p)
+{
+  *p = 42;
+  return 5;
+}
+
+struct S {
+  int x;
+  int y;
+  int z;
+  S() : x(foo (&y)), z(y) { } // { dg-bogus "uninitialized" }
+};
+
+struct T {
+  int x;
+  int y;
+  int z;
+  T() : x(({ y = 30; 42; })), z(y) { } // { dg-bogus "uninitialized" }
+};
+
+struct A {
+  int x, y, z;
+  int f () { y = 1; return 2; }
+  A ():
+    x (f ()),
+    z (y) // { dg-bogus "uninitialized" }
+  { }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-18.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-18.C
new file mode 100644
index 00000000000..29ae77a39f5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-18.C
@@ -0,0 +1,22 @@
+// PR c++/96121
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized" }
+
+struct A {
+  A();
+  int i;
+};
+struct B {
+  B(A);
+  int i;
+};
+
+struct composed2 {
+  B b_;
+  A a_;
+  composed2() : b_(a_) {} // { dg-warning "member .composed2::a_. is used uninitialized" }
+};
+
+composed2 test() {
+    return composed2{};
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-19.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-19.C
new file mode 100644
index 00000000000..e4d53d4bfba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-19.C
@@ -0,0 +1,50 @@
+// PR c++/19808
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized" }
+// Test we warn when initializing a base class.
+
+struct A {
+  A(int) { }
+};
+
+struct B : public A {
+  int x;
+  B() : A(x) { } // { dg-warning "member .B::x. is used uninitialized" }
+};
+
+struct C : public A {
+  int x;
+  int y;
+  C() : A(y = 4), x(y) { }
+};
+
+struct D : public A {
+  int x;
+  D() : A{x} { } // { dg-warning "member .D::x. is used uninitialized" }
+};
+
+struct E : public A {
+  int x;
+  int y;
+  E() : A{y = 4}, x(y) { }
+};
+
+struct F {
+  F(int&) { }
+};
+
+struct G : F {
+  int x;
+  G() : F(x) { }
+};
+
+struct H {
+  H(int *) { }
+};
+
+struct I : H {
+  int x;
+  int arr[2];
+  I() : H(&x) { }
+  I(int) : H(arr) { }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-20.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-20.C
new file mode 100644
index 00000000000..867c4da858f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-20.C
@@ -0,0 +1,16 @@
+// PR c++/96121
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized" }
+// Test we warn with delegating constructors.
+
+struct A {
+  A(int);
+  A(int &, int);
+  A(int (*)[1]) : A(x) { } // { dg-warning "21:member .A::x. is used uninitialized" }
+  A(int (*)[2]) : A(x, x) { } // { dg-warning "24:member .A::x. is used uninitialized" }
+  A(int (*)[3]) : A(x, 0) { }
+  A(int (*)[4]) : A{x} { } // { dg-warning "21:member .A::x. is used uninitialized" }
+  A(int (*)[5]) : A{x, x} { } // { dg-warning "24:member .A::x. is used uninitialized" }
+  A(int (*)[6]) : A{x, 0} { }
+  int x;
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-21.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-21.C
new file mode 100644
index 00000000000..57ca00ab042
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-21.C
@@ -0,0 +1,20 @@
+// PR c++/19808
+// { dg-do compile }
+// { dg-options "-Wuninitialized" }
+
+struct A {
+  int a;
+  int b;
+  A(int) {}
+};
+
+struct S {
+  A a;
+  A a2;
+  S() :
+    /* We don't warn here, because we consider partial initialization
+       as initializing the whole object.  */
+    a((a2.a = 42)),
+    a2(a2.a)
+  { }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-22.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-22.C
new file mode 100644
index 00000000000..89686a032eb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-22.C
@@ -0,0 +1,37 @@
+// PR c++/19808
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized -Winit-self" }
+// Test that we don't warn when initializing a reference, unless it's
+// self-init.
+
+struct R {
+  int &r;
+};
+
+struct S {
+  R r;
+  int a;
+  int &b;
+  int c;
+};
+
+struct X {
+  S s;
+  X() : s{ { s.a }, 1, s.c, 3} { }
+};
+
+struct A {
+  int &r;
+  A() : r{r} { } // { dg-warning ".A::r. is initialized with itself" }
+};
+
+struct B {
+  int &r;
+  int a;
+  B() : r{a} { }
+};
+
+struct C {
+  R x;
+  C() : x{x.r} { } // { dg-warning "member .C::x. is used uninitialized" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-23.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-23.C
new file mode 100644
index 00000000000..7cb2a9e4c08
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-23.C
@@ -0,0 +1,24 @@
+// PR c++/19808
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized" }
+// Test that we don't warn in an uninstantiated template.
+
+struct A {
+  int *fn() { return nullptr; }
+};
+
+template<typename T>
+struct B {
+  B() : p(a->fn()) { }
+  A *a;
+  int *p;
+};
+
+template<typename T>
+struct C {
+  C() : p(a->fn()) { } // { dg-warning "member .C<int>::a. is used uninitialized" }
+  A *a;
+  int *p;
+};
+
+C<int> c;
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-24.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-24.C
new file mode 100644
index 00000000000..e5dd4295a70
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-24.C
@@ -0,0 +1,89 @@
+// PR c++/19808
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized -Winit-self" }
+
+int fint(int);
+int fintp(int *);
+int fintr(int &);
+int fintcr(const int &);
+
+int arr[10];
+
+struct S {
+  int x;
+  int y;
+  const int z = 42;
+  int *p;
+
+  S(int (*)[1]) : x(x) { } // { dg-warning "initialized with itself" }
+  S(int (*)[2]) : x(x + x) { } // { dg-warning "member .S::x. is used uninitialized" }
+  S(int (*)[3]) : x(static_cast<int>(y)) { } // { dg-warning "member .S::y. is used uninitialized" }
+  S(int (*)[4]) : x(static_cast<int>(x)) { } // { dg-warning "member .S::x. is used uninitialized" }
+  S(int (*)[5]) : x(fint(x)) { }
+  S(int (*)[6]) : x(fint(y)) { }
+
+  S(int (*)[7]) : x(sizeof(x)) { }
+  S(int (*)[8]) : x(sizeof(y)) { }
+  S(int (*)[9]) : p(&x) { }
+  S(int (*)[10]) : x(fintp(&y)) { }
+  S(int (*)[11]) : x(fintr(y)) { }
+  S(int (*)[12]) : x(fintcr(y)) { }
+  S(int (*)[26]) : x(((void)(__typeof(y)) 1, 1)) { }
+  S(int (*)[27]) : x(((void)(decltype(y)) 1, 1)) { }
+  S(int (*)[28]) : x(__alignof__(y)) { }
+  S(int (*)[29]) : x(noexcept(y)) { }
+
+  S(int (*)[13]) : x(0), y(x ? y : y) { }
+  S(int (*)[14]) : x(0), y(1 + (x ? y : y)) { }
+  S(int (*)[15]) : x(-y) { } // { dg-warning "member .S::y. is used uninitialized" }
+  S(int (*)[16]) : x(1 << y) { } // { dg-warning "member .S::y. is used uninitialized" }
+  S(int (*)[17]) : x(this->y) { } // { dg-warning "member .S::y. is used uninitialized" }
+  S(int (*)[18]) : x(arr[y]) { } // { dg-warning "member .S::y. is used uninitialized" }
+  S(int (*)[19]) : x(0), y(x ? x : y) { }
+  S(int (*)[20]) : x(0), y(y ? x : y) { }
+  S(int (*)[21]) : x(0), y(y ? x : x) { }
+  S(int (*)[22]) : x(0), y((fint(y), x)) { }
+  S(int (*)[23]) : x(0), y(x += y) { } // "member .S::y. is used uninitialized" but too complex for the FE
+  S(int (*)[24]) : x(y += 10) { } // "member .S::y. is used uninitialized" but too complex for the FE
+  S(int (*)[25]) : x(y++) { } // { dg-warning "member .S::y. is used uninitialized" }
+};
+
+// Same, but { }.
+struct R {
+  int x;
+  int y;
+  const int z = 42;
+  int *p;
+
+  R(int (*)[1]) : x{x} { } // { dg-warning "member .R::x. is used uninitialized" }
+  R(int (*)[2]) : x{x + x} { } // { dg-warning "member .R::x. is used uninitialized" }
+  R(int (*)[3]) : x{static_cast<int>(y)} { } // { dg-warning "member .R::y. is used uninitialized" }
+  R(int (*)[4]) : x{static_cast<int>(x)} { } // { dg-warning "member .R::x. is used uninitialized" }
+  R(int (*)[5]) : x{fint(x)} { }
+  R(int (*)[6]) : x{fint(y)} { }
+
+  R(int (*)[7]) : x{sizeof(x)} { }
+  R(int (*)[8]) : x{sizeof(y)} { }
+  R(int (*)[9]) : p{&x} { }
+  R(int (*)[10]) : x{fintp(&y)} { }
+  R(int (*)[11]) : x{fintr(y)} { }
+  R(int (*)[12]) : x{fintcr(y)} { }
+  R(int (*)[26]) : x{((void)(__typeof(y)) 1, 1)} { }
+  R(int (*)[27]) : x{((void)(decltype(y)) 1, 1)} { }
+  R(int (*)[28]) : x{__alignof__(y)} { }
+  R(int (*)[29]) : x{noexcept(y)} { }
+
+  R(int (*)[13]) : x{0}, y{x ? y : y} { }
+  R(int (*)[14]) : x{0}, y{1 + (x ? y : y)} { }
+  R(int (*)[15]) : x{-y} { } // { dg-warning "member .R::y. is used uninitialized" }
+  R(int (*)[16]) : x{1 << y} { } // { dg-warning "member .R::y. is used uninitialized" }
+  R(int (*)[17]) : x{this->y} { } // { dg-warning "member .R::y. is used uninitialized" }
+  R(int (*)[18]) : x{arr[y]} { } // { dg-warning "member .R::y. is used uninitialized" }
+  R(int (*)[19]) : x{0}, y{x ? x : y} { }
+  R(int (*)[20]) : x{0}, y{y ? x : y} { }
+  R(int (*)[21]) : x{0}, y{y ? x : x} { }
+  R(int (*)[22]) : x{0}, y{(fint(y), x)} { }
+  R(int (*)[23]) : x{0}, y{x += y} { } // "member .R::y. is used uninitialized" but too complex for the FE
+  R(int (*)[24]) : x{y += 10} { } // "member .R::y. is used uninitialized" but too complex for the FE
+  R(int (*)[25]) : x{y++} { } // { dg-warning "member .R::y. is used uninitialized" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-25.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-25.C
new file mode 100644
index 00000000000..fb652f989a4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-25.C
@@ -0,0 +1,12 @@
+// PR c++/19808
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wall" }
+
+struct A {
+  A *a;
+};
+
+struct B : A {
+  int i;
+  B() : A{a} {}
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-26.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-26.C
new file mode 100644
index 00000000000..a887d12e9f9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-26.C
@@ -0,0 +1,22 @@
+// PR c++/19808
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized" }
+// Anonymous union/struct.
+// ??? The diagnostic should be improved to say 'b' instead of
+// "<anonymous>".
+
+struct S {
+  __extension__ struct {
+    int a;
+    int b;
+  };
+  S() : a(b) { } // { dg-warning "is used uninitialized" }
+};
+
+struct U {
+  union {
+    int a;
+    int b;
+  };
+  U() : a(b) { } // { dg-warning "is used uninitialized" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-27.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-27.C
new file mode 100644
index 00000000000..24e6b9b5b48
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-27.C
@@ -0,0 +1,20 @@
+// PR c++/19808
+// { dg-do compile }
+// { dg-options "-Wall" }
+
+enum E { red };
+
+struct C {
+  C(int *, unsigned);
+};
+
+template <unsigned U> struct D : C {
+  D(int, int, E) : C(e, U) {}
+  int e[2];
+};
+
+void
+g ()
+{
+  D<1>(0, 0, red);
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-28.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-28.C
new file mode 100644
index 00000000000..7dbbf8719ec
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-28.C
@@ -0,0 +1,59 @@
+// PR c++/19808
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized" }
+
+struct S {
+  int i, j, k, l;
+  S() : i(j), // { dg-warning "member .S::j. is used uninitialized" }
+	j(1),
+	k(l + 1), // { dg-warning "member .S::l. is used uninitialized" }
+	l(2) { }
+};
+
+struct A {
+  int a, b, c;
+  A() : a(b // { dg-warning "member .A::b. is used uninitialized" }
+	  + c) { } // { dg-warning "member .A::c. is used uninitialized" }
+};
+
+struct B {
+  int &r;
+  int *p;
+  int a;
+  B() : r(a), p(&a), a(1) { }
+};
+
+struct C {
+  const int &r1, &r2;
+  C () : r1(r2), // { dg-warning "reference .C::r2. is not yet bound to a value when used here" }
+	 r2(r1) { }
+};
+
+struct D {
+  int a = 1;
+  int b = 2;
+  D() : a(b + 1), b(a + 1) { } // { dg-warning "member .D::b. is used uninitialized" }
+};
+
+struct E {
+  int a = 1;
+  E() : a(a + 1) { } // { dg-warning "member .E::a. is used uninitialized" }
+};
+
+struct F {
+  int a = 1;
+  int b;
+  F() : b(a + 1) { }
+};
+
+struct bar {
+  bar() {}
+  bar(bar&) {}
+};
+
+class foo {
+  bar first;
+  bar second;
+public:
+  foo() : first(second) {} // { dg-warning "member .foo::second. is used uninitialized" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-29.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-29.C
new file mode 100644
index 00000000000..bc742997441
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-29.C
@@ -0,0 +1,59 @@
+// PR c++/19808
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wuninitialized" }
+
+struct S {
+  int i, j, k, l;
+  S() : i{j}, // { dg-warning "member .S::j. is used uninitialized" }
+	j{1},
+	k{l + 1}, // { dg-warning "member .S::l. is used uninitialized" }
+	l{2} { }
+};
+
+struct A {
+  int a, b, c;
+  A() : a{b // { dg-warning "member .A::b. is used uninitialized" }
+	  + c} { } // { dg-warning "member .A::c. is used uninitialized" }
+};
+
+struct B {
+  int &r;
+  int *p;
+  int a;
+  B() : r{a}, p{&a}, a{1} { }
+};
+
+struct C {
+  const int &r1, &r2;
+  C () : r1{r2}, // { dg-warning "reference .C::r2. is not yet bound to a value when used here" }
+	 r2{r1} { }
+};
+
+struct D {
+  int a = 1;
+  int b = 2;
+  D() : a{b + 1}, b{a + 1} { } // { dg-warning "member .D::b. is used uninitialized" }
+};
+
+struct E {
+  int a = 1;
+  E() : a{a + 1} { } // { dg-warning "member .E::a. is used uninitialized" }
+};
+
+struct F {
+  int a = 1;
+  int b;
+  F() : b{a + 1} { }
+};
+
+struct bar {
+  bar() {}
+  bar(bar&) {}
+};
+
+class foo {
+  bar first;
+  bar second;
+public:
+  foo() : first{second} {} // { dg-warning "member .foo::second. is used uninitialized" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wuninitialized-30.C b/gcc/testsuite/g++.dg/warn/Wuninitialized-30.C
new file mode 100644
index 00000000000..ba0f76ed936
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-30.C
@@ -0,0 +1,13 @@
+// PR c++/19808
+// { dg-do compile }
+// { dg-options "-Wuninitialized" }
+
+class diagnostic_event {
+public:
+  virtual int get_stack_depth();
+};
+struct event_range {
+  event_range(diagnostic_event &initial_event)
+      : m_stack_depth(initial_event.get_stack_depth()) {}
+  int m_stack_depth;
+};
diff --git a/gcc/tree.c b/gcc/tree.c
index 7bfd64160f4..15c064cc0ce 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -4781,6 +4781,7 @@ stabilize_reference (tree ref)
   TREE_READONLY (result) = TREE_READONLY (ref);
   TREE_SIDE_EFFECTS (result) = TREE_SIDE_EFFECTS (ref);
   TREE_THIS_VOLATILE (result) = TREE_THIS_VOLATILE (ref);
+  protected_set_expr_location (result, EXPR_LOCATION (ref));
 
   return result;
 }

base-commit: 206c08ce28a3c70afa4ecf9274611295d6369218
-- 
2.33.1



More information about the Gcc-patches mailing list