[PATCH] c++: Implement -Wuninitialized for mem-initializers (redux) [PR19808]
Marek Polacek
polacek@redhat.com
Mon Nov 8 21:17:54 GMT 2021
On Sun, Nov 07, 2021 at 04:48:46PM -0700, Martin Sebor wrote:
> On 11/5/21 5:22 PM, Marek Polacek via Gcc-patches 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.
>
> I (still) believe this is a great improvement :)
Yea, would be nice to finally nail it down this time around.
> Thinking about your last sentence above and experimenting with
> the patch just a little, we do know that function calls cannot
> "initialize" (by assigning a value) const or reference members,
> so those can be assumed to be uninitiazed when used before
> their initialized has been evaluated. As in:
>
> int f (int);
>
> struct A
> {
> int x;
> const int y;
> A ():
> x (f (y)), << y used before initialized
> y (1) { }
> };
>
> It should also be reasonable for the prurposes of warnings to
> assume that a call to a const member function (or any call where
> an object is passed by a const reference) doesn't change any
> non-mutable members. The middle end issues -Wmaybe-uninitialized
> when it sees an uninitualized object passed to a const-qualified
> pointer or reference.
Maybe, I don't know. It gets tricky and I don't think I want to
handle it in my patch, at least not in this initial version. It
may be best to leave this to the ME. Dunno.
> At the same time, a call to a non-const member function can assign
> a value to a not-yet-initialized member, so I wonder if its uses
> subsequent to it should be accepted without warning:
>
> struct A
> {
> int x, y, z;
> int f () { y = 1; return 2; }
> A ():
> x (f ()),
> z (y) << avoid warning here?
> { }
> };
This is actually a bug in my code, now fixed (made changes in the
DECL_NONSTATIC_MEMBER_FUNCTION_P block).
> > + /* 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);
>
> Might I suggest the word "member" instead?
>
> This is my OCD rearing its head but I always cringe when I read
> the word field in reference to a subobject. Field is a term
> common in database programming but in C and C++, either member
> or subobject are more wide-spread. Except for bit-fields, C
> formally uses the term field is to refer to the are into which
> printf directives format their output. With one exception, C++
> 98 used it the same way. I see that in recent versions another
> reference has crept in with the memory model, but I think it
> would be fair to call these two anomalies.
Hahah! No worries, changed to "member". "Field" is probably just
a GCCism in my head (due to FIELD_DECL).
> > +++ 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()) {}
>
> Here, because S::a.m isn't initialized when a.get() returns it,
> we ideally want a warning, we just can't get it, right?
>
> To make this clear to people who read tests and also easier to
> add a warning if GCC gets smart enough (without causing what
> might look like false positives), I'd suggest to write the tests
> so that they will not trigger these "ideal" warnings. (In
> the above, it should be doable simply by initializing A::m in
> A's ctors.)
Right, we don't warn though it wouldn't be wrong to warn. I've
added ": m{}" to make 'm' initialized.
Regtest with the updated patch running...
Marek
More information about the Gcc-patches
mailing list