From: Jason Merrill <jason@redhat.com>
To: Marek Polacek <polacek@redhat.com>,
GCC Patches <gcc-patches@gcc.gnu.org>
Subject: Re: [PATCH] c++: Implement -Wuninitialized for mem-initializers (redux) [PR19808]
Date: Sun, 7 Nov 2021 00:45:24 -0400 [thread overview]
Message-ID: <80f1345d-8d04-a948-e7d2-ce25ab0e4d1e@redhat.com> (raw)
In-Reply-To: <20211105232224.324169-1-polacek@redhat.com>
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
>
next prev parent reply other threads:[~2021-11-07 4:45 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-11-05 23:22 Marek Polacek
2021-11-07 4:45 ` Jason Merrill [this message]
2021-11-08 23:41 ` [PATCH v2] " Marek Polacek
2021-11-18 22:10 ` Jason Merrill
2021-11-19 0:24 ` Marek Polacek
2021-11-19 1:56 ` Jason Merrill
2021-11-19 15:29 ` Martin Sebor
2021-11-07 23:48 ` [PATCH] " Martin Sebor
2021-11-08 21:17 ` Marek Polacek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=80f1345d-8d04-a948-e7d2-ce25ab0e4d1e@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=polacek@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).