On Thu, 11 Nov 2021, Martin Sebor wrote:

> On 11/11/21 1:18 AM, Richard Biener wrote:
> > On Wed, 10 Nov 2021, Martin Sebor wrote:
> > 
> >> On 11/10/21 3:09 AM, Richard Biener via Gcc-patches wrote:
> >>> This XFAILs the bogus diagnostic test and rectifies the expectation
> >>> on the optimization.
> >>>
> >>> Tested on x86_64-unknown-linux-gnu, pushed.
> >>>
> >>> 2021-11-10  Richard Biener  <rguent...@suse.de>
> >>>
> >>>   PR testsuite/102690
> >>>   * g++.dg/warn/Warray-bounds-16.C: XFAIL diagnostic part
> >>>   and optimization.
> >>> ---
> >>>    gcc/testsuite/g++.dg/warn/Warray-bounds-16.C | 6 +++---
> >>>    1 file changed, 3 insertions(+), 3 deletions(-)
> >>>
> >>> diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-16.C
> >>> b/gcc/testsuite/g++.dg/warn/Warray-bounds-16.C
> >>> index 17b4d0d194e..89cbadb91c7 100644
> >>> --- a/gcc/testsuite/g++.dg/warn/Warray-bounds-16.C
> >>> +++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-16.C
> >>> @@ -19,11 +19,11 @@ struct S
> >>>        p = (int*) new unsigned char [sizeof (int) * m];
> >>>    
> >>>        for (int i = 0; i < m; i++)
> >>> -      new (p + i) int ();
> >>> +      new (p + i) int (); /* { dg-bogus "bounds" "pr102690" { xfail *-*-*
> >>> }
> >>> } */
> >>>      }
> >>>    };
> >>>    
> >>>    S a (0);
> >>>    
> >>> -/* Verify the loop has been eliminated.
> >>> -   { dg-final { scan-tree-dump-not "goto" "optimized" } } */
> >>> +/* The loop cannot be eliminated since the global 'new' can change 'm'.
> >>> */
> >>
> >> I don't understand this comment.  Can you please explain how
> >> the global operator new (i.e., the one outside the loop below)
> >> can change the member of the class whose ctor calls the new?
> >>
> >> The member, or more precisely the enclosing object, doesn't
> >> yet exist at the time the global new is called because its
> >> ctor hasn't finished, so nothing outside the ctor can access
> >> it.  A pointer to the S under construction can be used (and
> >> could be accessed by a replacement new) but it cannot be
> >> dereferenced to access its members because the object it
> >> points to doesn't exist until after the ctor completes.
> > 
> > Yes, that's the C++ legalise - which is why I XFAILed that
> > part of the test rather than just removed it.  The middle-end
> > sees the object *this as existing and being global, thus
> > accessible and mutable by '::new' which when replaced by
> > the user could access and alter *this.  Like maybe for
> > 
> > S s;
> > 
> > void *operator new(..) { s.m = 0; }
> > 
> > main()
> > {
> >    new (&s) (1);
> > }
> > 
> > that may be invalid C++ but this detail of C++ is not
> > reflected in the GIMPLE IL.  Before the change that regressed
> > this if S::S() would call a global function foo() instead
> > of new to do the allocation the behavior would be as after
> > the change.  Isn't the call to new or foo part of the
> > construction and as such obviously allowed to access
> > and alter the in-construction object?
> 
> Here's my understanding.
> 
> The lifetime of an object ends when its storage is reused to
> create another object of a class type, and the lifetime of
> the other object begins when its initialization is complete.
> 
> In the window between the new ctor starting and completing
> the new object can be accessed in limited ways, but not
> the old object.  (If it were otherwise, imagine the new
> object's ctor throwing in the middle of constructing the new
> object.  What state would that leave the old object in?  Which
> members could still be accessed?)
> 
> The only way to access a subobject of the new object while
> it's still under construction is through a pointer to that
> subobject or through this (or a pointer derive from this).

OK, so that might be the detail that one could exploit eventually.

> It seems that it should be possible to capture the constraint
> in the middle end that no member of an object under construction
> can be accessed unless a pointer to it has escaped that's derived
> from the this pointer.

IIRC at some point we had this * restrict qualified in the CTOR
but that broke cases where that's clearly not correct (but I forgot
the details).  Note for the middle-end that would just point at
another missed optimization, namely that restrict does not work
to disambiguate against calls.

> But this seems like a sufficiently obscure case that an expert
> on the C++ object model should confirm it.
>
> Martin
> 
> > 
> >> I copy the test below:
> >>
> >> inline void* operator new (__SIZE_TYPE__, void * v)
> >> {
> >>    return v;
> >> }
> >>
> >> struct S
> >> {
> >>    int* p;
> >>    int m;
> >>
> >>    S (int i)
> >>    {
> >>      m = i;
> >>      p = (int*) new unsigned char [sizeof (int) * m];
> >>
> >>      for (int i = 0; i < m; i++)
> >>        new (p + i) int (); /* { dg-bogus "bounds" "pr102690" { xfail *-*-*
> >> } }
> >> */
> >>    }
> >> };
> >>
> >> S a (0);
> >>
> >> Thanks
> >> Martin
> >>
> > 
> 
> 
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Ivo Totev; HRB 36809 (AG Nuernberg)

Reply via email to