On Thu, Nov 28, 2019 at 10:28 PM Marek Polacek <pola...@redhat.com> wrote:

> On Wed, Nov 27, 2019 at 10:47:58PM -0500, Jason Merrill wrote:
> > On 11/27/19 6:35 PM, Marek Polacek wrote:
> > > On Wed, Nov 27, 2019 at 04:47:01PM -0500, Jason Merrill wrote:
> > > > On 11/27/19 2:36 PM, Marek Polacek wrote:
> > > > > On Sun, Nov 24, 2019 at 12:24:48PM -0500, Jason Merrill wrote:
> > > > > > On 11/16/19 5:23 PM, Marek Polacek wrote:
> > > > > > > [ Working virtually on Baker Island. ]
> > > > > > >
> > > > > > > This patch implements C++20 P1331, allowing trivial default
> initialization in
> > > > > > > constexpr contexts.
> > > > > > >
> > > > > > > I used Jakub's patch from the PR which allowed uninitialized
> variables in
> > > > > > > constexpr contexts.  But the hard part was handling
> CONSTRUCTOR_NO_CLEARING
> > > > > > > which is always cleared in cxx_eval_call_expression.  We need
> to set it in
> > > > > > > the case a constexpr constructor doesn't initialize all the
> members, so that
> > > > > > > we can give proper diagnostic instead of value-initializing.
> A lot of my
> > > > > > > attempts flopped but then I came up with this approach, which
> handles various
> > > > > > > cases as tested in constexpr-init8.C, where S is initialized
> by a non-default
> > > > > > > constexpr constructor, and constexpr-init9.C, using delegating
> constructors.
> > > > > > > And the best part is that I didn't need any new
> cx_check_missing_mem_inits
> > > > > > > calls!  Just save the information whether a constructor is
> missing an init
> > > > > > > into constexpr_fundef_table and retrieve it when needed.
> > > > > >
> > > > > > Is it necessary to clear the flag for constructors that do
> happen to
> > > > > > initialize all the members?  I would think that leaving that
> clearing to
> > > > > > reduced_constant_expression_p would be enough.
> > > > >
> > > > > It seems so: if I tweak cxx_eval_call_expression to only call
> clear_no_implicit_zero
> > > > > when 'fun' isn't DECL_CONSTRUCTOR_P, then a lot breaks, e.g.
> constexpr-base.C
> > > > > where the constructor initializes all the members.  By breaking I
> mean spurious
> > > > > errors coming from
> > > > >
> > > > > 5937   if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING
> (r))
> > > > > 5938     {
> > > > > 5939       if (!allow_non_constant)
> > > > > 5940         error ("%qE is not a constant expression because it
> refers to "
> > > > > 5941                "an incompletely initialized variable", t);
> > > > > 5942       TREE_CONSTANT (r) = false;
> > > > > 5943       non_constant_p = true;
> > > > > 5944     }
> > > >
> > > > Why didn't reduced_constant_expression_p unset
> CONSTRUCTOR_NO_CLEARING?
> > >
> > > We have a constructor that initializes a base class and members of a
> class:
> > >
> > >    {.D.2364={.i=12}, .a={.i=24}, .j=36}
> > >
> > > Say we don't clear CONSTRUCTOR_NO_CLEARING in this ctor in
> cxx_eval_call_expression.
> > > Then soon in reduced_constant_expression_p we do
> > > 2221             field = next_initializable_field (TYPE_FIELDS
> (TREE_TYPE (t)));
> > > and since "Implement P0017R1, C++17 aggregates with bases. / r241187"
> we skip
> > > base fields in C++17 so 'field' is set to 'a'.
> >
> > Hmm?
> >
> > > next_initializable_field (tree field)
> > > {
> > >   while (field
> > >          && (TREE_CODE (field) != FIELD_DECL
> > >              || DECL_UNNAMED_BIT_FIELD (field)
> > >              || (DECL_ARTIFICIAL (field)
> > >                  && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE
> (field)))))
> > >     field = DECL_CHAIN (field);
> >
> > This skips artificial fields except that in C++17 and up base fields are
> > *not* skipped.
> >
> > How are you getting field starting with 'a'?  Are you compiling in a
> lower
> > standard mode?  The code using next_initializable_field doesn't work for
> > lower -std because of skipping base fields.
>
> Duh, I'm sorry, you're right of course, I got it backwards.  I didn't
> realize
> I was debugging without -std=c++2a :/
>
> > So perhaps we want to always clear_no_implicit_zero before c++20, and
> always
> > for c++20 and up?
>
> This doesn't work for constexpr-init8.C, where S is initialized by a
> non-default
> constexpr constructor:
>
>   struct S {
>     constexpr S(int) {}
>   };
>
>   struct W {
>     constexpr W(int) : s(8), p() {}
>
>     S s;
>     int *p;
>   };
>
>   constexpr auto a = W(42);
>
> When we perform register_constexpr_fundef the result of
> massage_constexpr_body is
>
>   {.s=S::S (&((struct W *) this)->s, NON_LVALUE_EXPR <8>), .p=0B}
>
> i.e. a ctor that initializes all the members.  But later
> reduced_constant_expression_p only ever sees {.p=0B} which seemingly
> doesn't
> initialize all the members, so CONSTRUCTOR_NO_CLEARING is not cleared, and
> we
> give an error.
>

Sounds like reduced_constant_expression_p needs to deal better with empty
bases.

Jason

Reply via email to