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

Jason Merrill jason@redhat.com
Sun Nov 7 04:45:24 GMT 2021


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.

> 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.
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> 
> 	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.
> 	(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.
> ---
>   gcc/cp/init.c                                 | 187 ++++++++++++++++--
>   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 |  24 +++
>   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/tree.c                                    |   1 +
>   19 files changed, 802 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
> 
> diff --git a/gcc/cp/init.c b/gcc/cp/init.c
> index 771a19bc402..c39f66a4deb 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,126 @@ 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:
> +    /* 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 may have side effects and initialize other members; it's
> +	 not the front end's job to try to figure it out.  */
> +      else
> +	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,
> +			"field %qD is used uninitialized", field);
> +	  *walk_subtrees = false;
> +	}
> +    }
> +
> +  return NULL_TREE;
> +
> +give_up:
> +  *walk_subtrees = false;
> +  uninitialized->empty ();
> +  return integer_zero_node;
> +}
> +
>   /* 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 +922,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 +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.

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

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.

> +	uninitialized.add (field);
> +
>     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));
> +      if (!uninitialized.is_empty ())
> +	{
> +	  find_uninit_data data = { &uninitialized, current_class_type };
> +	  cp_walk_tree_without_duplicates (&ctor,
> +					   find_uninit_fields_r,
> +					   &data);
> +	}
>         return;
>       }
>   
> @@ -1378,6 +1528,15 @@ emit_mem_initializers (tree mem_inits)
>   			      flags,
>                                 tf_warning_or_error);
>   	  expand_cleanup_for_base (subobject, NULL_TREE);
> +	  if (!uninitialized.is_empty ()
> +	      && STATEMENT_LIST_TAIL (cur_stmt_list))
> +	    {
> +	      tree last = STATEMENT_LIST_TAIL (cur_stmt_list)->stmt;
> +	      find_uninit_data data = { &uninitialized,
> +					BINFO_TYPE (subobject) };
> +	      cp_walk_tree_without_duplicates (&last, find_uninit_fields_r,
> +					       &data);
> +	    }
>   	}
>         else if (!ABSTRACT_CLASS_TYPE_P (current_class_type))
>   	/* C++14 DR1658 Means we do not have to construct vbases of
> @@ -1405,7 +1564,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 9fb74d34920..018248f07f4 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..62052e7d3c7
> --- /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() { }
> +  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..3e06f6ebae4
> --- /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 "field .F::e. is used uninitialized" }
> +  F(int (*)[2]) : a(e.num()) { }
> +  F(int (*)[3]) : e(a) { } // { dg-warning "field .F::a. is used uninitialized" }
> +  F(int (*)[4]) : a(4), e(a) { }
> +  F(int (*)[5]) : e(b) { } // { dg-warning "field .F::b. is used uninitialized" }
> +  F(int (*)[6]) : e(b), b(4) { } // { dg-warning "field .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 "field .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 "field .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..e76a86ca274
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wuninitialized-17.C
> @@ -0,0 +1,24 @@
> +// 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" }
> +};
> 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..c05ad42f95e
> --- /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 "field .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..c401f8636bf
> --- /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 "field .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 "field .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..9f367f0fdfd
> --- /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:field .A::x. is used uninitialized" }
> +  A(int (*)[2]) : A(x, x) { } // { dg-warning "24:field .A::x. is used uninitialized" }
> +  A(int (*)[3]) : A(x, 0) { }
> +  A(int (*)[4]) : A{x} { } // { dg-warning "21:field .A::x. is used uninitialized" }
> +  A(int (*)[5]) : A{x, x} { } // { dg-warning "24:field .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..003fdd300c2
> --- /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 "field .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..db3778300be
> --- /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 "field .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..3f81d52fa69
> --- /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 "field .S::x. is used uninitialized" }
> +  S(int (*)[3]) : x(static_cast<int>(y)) { } // { dg-warning "field .S::y. is used uninitialized" }
> +  S(int (*)[4]) : x(static_cast<int>(x)) { } // { dg-warning "field .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 "field .S::y. is used uninitialized" }
> +  S(int (*)[16]) : x(1 << y) { } // { dg-warning "field .S::y. is used uninitialized" }
> +  S(int (*)[17]) : x(this->y) { } // { dg-warning "field .S::y. is used uninitialized" }
> +  S(int (*)[18]) : x(arr[y]) { } // { dg-warning "field .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) { } // "field .S::y. is used uninitialized" but too complex for the FE
> +  S(int (*)[24]) : x(y += 10) { } // "field .S::y. is used uninitialized" but too complex for the FE
> +  S(int (*)[25]) : x(y++) { } // { dg-warning "field .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 "field .R::x. is used uninitialized" }
> +  R(int (*)[2]) : x{x + x} { } // { dg-warning "field .R::x. is used uninitialized" }
> +  R(int (*)[3]) : x{static_cast<int>(y)} { } // { dg-warning "field .R::y. is used uninitialized" }
> +  R(int (*)[4]) : x{static_cast<int>(x)} { } // { dg-warning "field .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 "field .R::y. is used uninitialized" }
> +  R(int (*)[16]) : x{1 << y} { } // { dg-warning "field .R::y. is used uninitialized" }
> +  R(int (*)[17]) : x{this->y} { } // { dg-warning "field .R::y. is used uninitialized" }
> +  R(int (*)[18]) : x{arr[y]} { } // { dg-warning "field .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} { } // "field .R::y. is used uninitialized" but too complex for the FE
> +  R(int (*)[24]) : x{y += 10} { } // "field .R::y. is used uninitialized" but too complex for the FE
> +  R(int (*)[25]) : x{y++} { } // { dg-warning "field .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..4e51b4b09f2
> --- /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 "field .S::j. is used uninitialized" }
> +	j(1),
> +	k(l + 1), // { dg-warning "field .S::l. is used uninitialized" }
> +	l(2) { }
> +};
> +
> +struct A {
> +  int a, b, c;
> +  A() : a(b // { dg-warning "field .A::b. is used uninitialized" }
> +	  + c) { } // { dg-warning "field .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 "field .D::b. is used uninitialized" }
> +};
> +
> +struct E {
> +  int a = 1;
> +  E() : a(a + 1) { } // { dg-warning "field .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 "field .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..f21760f4e70
> --- /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 "field .S::j. is used uninitialized" }
> +	j{1},
> +	k{l + 1}, // { dg-warning "field .S::l. is used uninitialized" }
> +	l{2} { }
> +};
> +
> +struct A {
> +  int a, b, c;
> +  A() : a{b // { dg-warning "field .A::b. is used uninitialized" }
> +	  + c} { } // { dg-warning "field .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 "field .D::b. is used uninitialized" }
> +};
> +
> +struct E {
> +  int a = 1;
> +  E() : a{a + 1} { } // { dg-warning "field .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 "field .foo::second. is used uninitialized" }
> +};
> 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: bcf3728abe8488882922005166d3065fc5fdfea1
> 



More information about the Gcc-patches mailing list