[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