Bug 111123 - Warning about "used uninitialized" member shown or hidden randomly
Summary: Warning about "used uninitialized" member shown or hidden randomly
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 13.2.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
Depends on:
Blocks: Wuninitialized
  Show dependency treegraph
 
Reported: 2023-08-23 19:57 UTC by Andrea Griffini
Modified: 2023-08-31 20:23 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2023-08-24 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Andrea Griffini 2023-08-23 19:57:02 UTC
This code should warning (with -Wall) about the use of `border` that is uninitialized

    #include <stdio.h>
    #include <vector>

    struct Camera {
        struct P2d {
            float x, y;
        };
        std::vector<P2d> clip_area;
        float border = 10.f;
        int z = 3;
        Camera() : clip_area({{border, border},
                              {1-border, border},
                              {1-border, 1-border},
                              {border, 1-border}})
        { }
    };

    int main() {
        Camera c;
        printf("%.18g\n", c.clip_area[0].x);
    }

However does so only if member `z` is present; commenting out the line `int z = 3;` silences the warning.

This show/hide of the warning happens also pseudo-randomly in other cases (while I was trying to get the minimum code showing the problem I found many cases in which removing even an executable statement in the body of a method triggered the behavior change).
Comment 1 Andrea Griffini 2023-08-23 20:12:30 UTC
Forgot to say that -O3 is needed to see the warning (this is however expected)
Comment 2 Richard Biener 2023-08-24 06:52:48 UTC
There's a change in inlining when z is removed.  We then have the separate CTOR

void Camera::Camera (struct Camera * const this)
{
...
 <bb 2> [local count: 1073741824]:
  _2 = *this_6(D).border;
  _3 = 1.0e+0 - _2;
  _68 = {_2, _2, _3, _2};

and fno CLOBBER of *this_6(D) at its start at the time we do late uninit
diagnostics.  That's because we remove those indirect clobbers too early.
Comment 3 Richard Biener 2023-08-24 07:14:03 UTC
Adding [[gnu::noinline]] to Camera::Camera will never warn.
Comment 4 Richard Biener 2023-08-24 07:36:04 UTC
The interesting thing is that enabling the middle-end diagnostic to trigger shows we emit duplicate diagnostics:

struct Camera {
    float clip_area;
    float border = 10.f;
    [[gnu::noinline]] Camera() : clip_area(border) { }
};

Camera foo()
{
  Camera c;
  return c;
}

emits

t.C: In constructor 'Camera::Camera()':
t.C:4:44: warning: member 'Camera::border' is used uninitialized [-Wuninitialized]
    4 |     [[gnu::noinline]] Camera() : clip_area(border) { }
      |                                            ^~~~~~
t.C: In constructor 'Camera::Camera()':
t.C:4:44: warning: '*this.Camera::border' is used uninitialized [-Wuninitialize]
    4 |     [[gnu::noinline]] Camera() : clip_area(border) { }
      |                                            ^~~~~~

the first is from the C++ FE find_uninit_fields diagnostic which for some
reason doesn't work for the testcase in the description, possibly
the initializer list(?) isn't handled?  The early uninit IL is

  <bb 2> :
  MEM[(struct __as_base  &)this_6(D)] ={v} {CLOBBER};
  _1 = &this_6(D)->clip_area;
  std::allocator<Camera::P2d>::allocator (&D.26049);
  _2 = this_6(D)->border;
  D.26047[0].x = _2;
  _3 = this_6(D)->border;
  D.26047[0].y = _3;
  D.27007._M_array = &D.26047;
  D.27007._M_len = 1;
  std::vector<Camera::P2d>::vector (_1, D.27007, &D.26049);

the call to std::allocator<Camera::P2d>::allocator is thought to clobber
*this and thus possibly initialize 'border' here.

I'm testing a middle-end fix here - Marek, can you see whether it's possible
to detect this in the frontend?  The middle-end will require optimization.
The duplicate diagnostic might also be interesting to look at, but that
might already be reported separately?
Comment 5 GCC Commits 2023-08-24 13:11:58 UTC
The master branch has been updated by Richard Biener <rguenth@gcc.gnu.org>:

https://gcc.gnu.org/g:abf915193fbf725bb359e6936e10dcc282eb94cc

commit r14-3460-gabf915193fbf725bb359e6936e10dcc282eb94cc
Author: Richard Biener <rguenther@suse.de>
Date:   Thu Aug 24 09:32:54 2023 +0200

    tree-optimization/111123 - indirect clobbers thrown away too early
    
    The testcase in the PR shows that late uninit diagnostic relies
    on indirect clobbers in CTORs but we throw those away in the fab
    pass which is too early.  The reasoning was they were supposed
    to keep SSA names live but that's no longer the case since DCE
    doesn't treat them as keeping SSA uses live.
    
    The following instead removes them before out-of-SSA coalescing
    which is the thing that's still affected by them.
    
            PR tree-optimization/111123
            * tree-ssa-ccp.cc (pass_fold_builtins::execute): Do not
            remove indirect clobbers here ...
            * tree-outof-ssa.cc (rewrite_out_of_ssa): ... but here.
            (remove_indirect_clobbers): New function.
    
            * g++.dg/warn/Wuninitialized-pr111123-1.C: New testcase.
Comment 6 Richard Biener 2023-08-24 13:13:01 UTC
middle-end part are fixed, so with optimization we should diagnose this more consistently now.  Leaving open for -O0 and the C++ FE issue.