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

Jason Merrill jason@redhat.com
Thu Nov 18 22:10:47 GMT 2021


On 11/8/21 18:41, Marek Polacek wrote:
> 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...

Certainly not as part of this patch, but I wouldn't expect it to be 
expensive; the inline functions in question are small.

>>> @@ -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);

I wonder about flipping the sense of the set, so that it tracks fields 
that have been initialized rather than those that haven't; then you 
wouldn't need this loop.

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



More information about the Gcc-patches mailing list