On Wed, 18 Mar 2020, Jason Merrill wrote:

> On 3/18/20 11:58 AM, Patrick Palka wrote:
> > On Wed, 18 Mar 2020, Patrick Palka wrote:
> > 
> > > On Tue, 17 Mar 2020, Jason Merrill wrote:
> > > 
> > > > On 3/16/20 1:39 PM, Patrick Palka wrote:
> > > > > In this PR, we are performing constexpr evaluation of a CONSTRUCTOR of
> > > > > type
> > > > > union U which looks like
> > > > > 
> > > > >     {.a=foo (&<PLACEHOLDER_EXPR union U>)}.
> > > > > 
> > > > > Since the function foo takes a reference to the CONSTRUCTOR we're
> > > > > building,
> > > > > it
> > > > > could potentially modify the CONSTRUCTOR from under us.  In particular
> > > > > since
> > > > > U
> > > > > is a union, the evaluation of a's initializer could change the active
> > > > > member
> > > > > from a to another member -- something which cxx_eval_bare_aggregate
> > > > > doesn't
> > > > > expect to happen.
> > > > > 
> > > > > Upon further investigation, it turns out this issue is not limited to
> > > > > constructors of UNION_TYPE and not limited to cxx_eval_bare_aggregate
> > > > > either.
> > > > > For example, within cxx_eval_store_expression we may be evaluating an
> > > > > assignment
> > > > > such as (this comes from the test pr94066-2.C):
> > > > > 
> > > > >     ((union U *) this)->a = TARGET_EXPR <D.2163, foo ((union U *)
> > > > > this)>;
> > > > 
> > > > I assume this is actually an INIT_EXPR, or we would have preevaluated
> > > > and not
> > > > had this problem.
> > > 
> > > Yes exactly, I should have specified that the above is an INIT_EXPR and
> > > not a MODIFY_EXPR.
> > > 
> > > > 
> > > > > where evaluation of foo could change the active member of *this, which
> > > > > was
> > > > > set
> > > > > earlier in cxx_eval_store_expression to 'a'.  And if U is a
> > > > > RECORD_TYPE,
> > > > > then
> > > > > evaluation of foo could add new fields to *this, thereby making stale
> > > > > the
> > > > > 'valp'
> > > > > pointer to the target constructor_elt through which we're later
> > > > > assigning.
> > > > > 
> > > > > So in short, it seems that both cxx_eval_bare_aggregate and
> > > > > cxx_eval_store_expression do not anticipate that a constructor_elt's
> > > > > initializer
> > > > > could modify the underlying CONSTRUCTOR as a side-effect.
> > > > 
> > > > Oof.  If this is well-formed, it's because initialization of a doesn't
> > > > actually start until the return statement of foo, so we're probably
> > > > wrong to
> > > > create a CONSTRUCTOR to hold the value of 'a' before evaluating foo.
> > > > Perhaps
> > > > init_subob_ctx shouldn't preemptively create a CONSTRUCTOR, and
> > > > similarly for
> > > > the cxx_eval_store_expression !preeval code.
> > > 
> > > Hmm, I think I see what you mean.  I'll look into this.
> > 
> > In cpp0x/constexpr-array12.C we have
> > 
> >      struct A { int ar[3]; };
> >      constexpr A a1 = { 0, a1.ar[0] };
> > 
> > the initializer for a1 is a CONSTRUCTOR with the form
> > 
> >      {.ar={0, (int) VIEW_CONVERT_EXPR<const struct A>(a1).ar[0]}}
> > 
> > If we don't preemptively create a CONSTRUCTOR in cxx_eval_bare_aggregate
> > to hold the value of 'ar' before evaluating its initializer, then we
> > won't be able to resolve the 'a1.ar[0]' later on, and we will reject
> > this otherwise valid test case with an "accessing an uninitialized array
> > element" diagnostic.  So it seems we need to continue creating a
> > CONSTRUCTOR in cxx_eval_bare_aggregate before evaluating the initializer
> > of an aggregate sub-object to handle self-referential CONSTRUCTORs like
> > the one above.
> > 
> > Then again, clang is going with rejecting the original testcase with the
> > following justification: https://bugs.llvm.org/show_bug.cgi?id=45133#c1
> > Should we follow suit?
> 
> Yes, let's.  There's no need to bend over backward to allow this kind of
> pathological testcase.  Hubert is proposing that the initialization of u.a
> starts with the call to foo, and the testcase has undefined behavior because
> it ends the lifetime of u.a in the middle of its initialization.

Got it.  I filed PR c++/94219 to track the testcase that uses a struct
in place of the union, on which we also ICE but this ICE is not a
regression from what I can tell.  For GCC 10 I'll work on a minimal
patch to reject the original testcase from PR c++/94066.

Reply via email to