On Fri, 6 May 2022, Jason Merrill wrote:

> On 5/6/22 11:22, Patrick Palka wrote:
> > Here ever since r10-7313-gb599bf9d6d1e18, reduced_constant_expression_p
> > in C++11/14 is rejecting the marked sub-aggregate initializer (of type S)
> > 
> >    W w = {.D.2445={.s={.D.2387={.m=0}, .b=0}}}
> >                       ^
> > 
> > ultimately because said initializer has CONSTRUCTOR_NO_CLEARING set, and
> > so the function proceeds to verify that all fields of S are initialized.
> > And before C++17 we don't expect to see base class fields (since
> > next_initializable_field skips over the), so the base class initializer
> > causes r_c_e_p to return false.
> 
> That seems like the primary bug.  I guess r_c_e_p shouldn't be using
> next_initializable_field.  Really that function should only be used for
> aggregates.

I see, I'll try replacing it in r_c_e_p.  Would that be in addition to
or instead of the clear_no_implicit_zero approach?

> 
> > The base class initializer comes from
> > the constructor call S::S(int).
> > 
> > The reason this is caused by r10-7313-gb599bf9d6d1e18 is because in that
> > commit we began using CONSTRUCTOR_NO_CLEARING to also track whether we're
> > in middle of activating a union member.  This overloaded use of the flag
> > affects clear_no_implicit_zero, which recurses into sub-aggregate
> > initializers only if the outer initializer has CONSTRUCTOR_NO_CLEARING
> > set.
> 
> Is that really overloaded?  In both union and non-union cases, it indicates
> that the object is not completely initialized.

Ah yes, makes sense.  More accurately the immediate clearing of
CONSTRUCTOR_NO_CLEARING after activating a union member at the end of
cxx_eval_store_expression is what affects clear_no_implicit_zero later.

> 
> > After that commit, the outer union initializer above no longer has
> > the flag set at this point and so clear_no_implicit_zero no longer clears
> > CONSTRUCTOR_NO_CLEARING for the marked inner initializer.
> 
> Why wasn't it cleared for the inner initializer as part of that evaluation?

Looks like the inner initializer {.D.2387={.m=0}, .b=0} is formed during
the subobject constructor call:

  V::V (&((struct S *) this)->D.2120);

after the evaluation of which, 'result' in cxx_eval_call_expression is NULL
(presumably because it's a CALL_EXPR, not AGGR_INIT_EXPR?):

  /* This can be null for a subobject constructor call, in
     which case what we care about is the initialization
     side-effects rather than the value.  We could get at the
     value by evaluating *this, but we don't bother; there's
     no need to put such a call in the hash table.  */
  result = lval ? ctx->object : ctx->ctor;

so we end up not calling clear_no_implicit_zero for the inner initializer
directly.  We only call clear_no_implicit_zero after evaluating the
AGGR_INIT_EXPR for outermost initializer (of type W).

> 
> > This patch fixes this by restoring the recursive behavior of
> > clear_no_implicit_zero for union initializers.  Arguably we should
> > we could improve reduced_constant_expression_p to accept the marked
> > initializer in C++11/14 even if it has CONSTRUCTOR_NO_CLEARING set, but
> > adjusting clear_no_implicit_zero seems safer to backport.
> > 
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> > trunk/12/11/10?
> > 
> >     PR c++/105491
> > 
> > gcc/cp/ChangeLog:
> > 
> >     * constexpr.cc (clear_no_implicit_zero): Recurse into a union
> >     initializer even if CONSTRUCTOR_NO_CLEARING is already cleared.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> >     * g++.dg/cpp0x/constexpr-union7.C: New test.
> >     * g++.dg/cpp0x/constexpr-union7a.C: New test.
> >     * g++.dg/cpp2a/constinit17.C: New test.
> > ---
> >   gcc/cp/constexpr.cc                           |  7 +++++-
> >   gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C | 17 +++++++++++++
> >   .../g++.dg/cpp0x/constexpr-union7a.C          | 15 ++++++++++++
> >   gcc/testsuite/g++.dg/cpp2a/constinit17.C      | 24 +++++++++++++++++++
> >   4 files changed, 62 insertions(+), 1 deletion(-)
> >   create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C
> >   create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C
> >   create mode 100644 gcc/testsuite/g++.dg/cpp2a/constinit17.C
> > 
> > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
> > index 9b1e71857fc..75fecbcbcb7 100644
> > --- a/gcc/cp/constexpr.cc
> > +++ b/gcc/cp/constexpr.cc
> > @@ -1886,7 +1886,12 @@ cxx_eval_internal_function (const constexpr_ctx *ctx,
> > tree t,
> >   static void
> >   clear_no_implicit_zero (tree ctor)
> >   {
> > -  if (CONSTRUCTOR_NO_CLEARING (ctor))
> > +  if (CONSTRUCTOR_NO_CLEARING (ctor)
> > +      /* For a union initializer, the flag could already be cleared but not
> > +    necessarily yet for its sub-aggregates, since for unions the flag is
> > +    also used by cxx_eval_store_expression to track whether we're in the
> > +    middle of activating one of its members.  */
> > +      || TREE_CODE (TREE_TYPE (ctor)) == UNION_TYPE)
> >       {
> >         CONSTRUCTOR_NO_CLEARING (ctor) = false;
> >         for (auto &e: CONSTRUCTOR_ELTS (ctor))
> > diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C
> > b/gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C
> > new file mode 100644
> > index 00000000000..b3147d9db50
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C
> > @@ -0,0 +1,17 @@
> > +// PR c++/105491
> > +// { dg-do compile { target c++11 } }
> > +
> > +struct V {
> > +  int m = 0;
> > +};
> > +struct S : V {
> > +  constexpr S(int) : b() { }
> > +  bool b;
> > +};
> > +struct W {
> > +  constexpr W() : s(0) { }
> > +  union {
> > +    S s;
> > +  };
> > +};
> > +constexpr W w;
> > diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C
> > b/gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C
> > new file mode 100644
> > index 00000000000..b676e7d1748
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C
> > @@ -0,0 +1,15 @@
> > +// PR c++/105491
> > +// { dg-do compile { target c++11 } }
> > +
> > +struct V {
> > +  int m = 0;
> > +};
> > +struct S : V {
> > +  constexpr S(int) : b() { }
> > +  bool b;
> > +};
> > +union W {
> > +  constexpr W() : s(0) { }
> > +  S s;
> > +};
> > +constexpr W w;
> > diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit17.C
> > b/gcc/testsuite/g++.dg/cpp2a/constinit17.C
> > new file mode 100644
> > index 00000000000..6431654ac85
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp2a/constinit17.C
> > @@ -0,0 +1,24 @@
> > +// PR c++/105491
> > +// { dg-do compile { target c++11 } }
> > +
> > +class Message {
> > +  virtual int GetMetadata();
> > +};
> > +class ProtobufCFileOptions : Message {
> > +public:
> > +  constexpr ProtobufCFileOptions(int);
> > +  bool no_generate_;
> > +  bool const_strings_;
> > +  bool use_oneof_field_name_;
> > +  bool gen_pack_helpers_;
> > +  bool gen_init_helpers_;
> > +};
> > +constexpr ProtobufCFileOptions::ProtobufCFileOptions(int)
> > +    : no_generate_(), const_strings_(), use_oneof_field_name_(),
> > +      gen_pack_helpers_(), gen_init_helpers_() {}
> > +struct ProtobufCFileOptionsDefaultTypeInternal {
> > +  constexpr ProtobufCFileOptionsDefaultTypeInternal() : _instance({}) {}
> > +  union {
> > +    ProtobufCFileOptions _instance;
> > +  };
> > +} __constinit _ProtobufCFileOptions_default_instance_;
> 
> 

Reply via email to