diff mbox series

[C++] c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.

Message ID 20191116222337.GJ3791@redhat.com
State New
Headers show
Series [C++] c++/91353 - P1331R2: Allow trivial default init in constexpr contexts. | expand

Commit Message

Marek Polacek Nov. 16, 2019, 10:23 p.m. UTC
[ 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.

constexpr-init10.C demonstrates that we can now elide a constructor call,
this is caused by the walk_field_subobs hunk.  I hope that's OK.

(No attempts to fix DR 2256 were made.)

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2019-11-16  Marek Polacek  <polacek@redhat.com>
	    Jakub Jelinek  <jakub@redhat.com>

	PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
	* c-cppbuiltin.c (c_cpp_builtins): Adjust the value of __cpp_constexpr.

	* class.c (trivial_default_constructor_is_constexpr): Return true in
	C++20.
	* constexpr.c (constexpr_fundef): Add a bool member.
	(retrieve_constexpr_fundef): Initialize it.
	(cx_check_missing_mem_inits): New pointer-to-bool parameter, with a
	default value NULL.  Allow missing field initializers in C++20.
	(register_constexpr_fundef): Adjust a call to
	cx_check_missing_mem_inits.  Set missing_init.
	(cxx_eval_call_expression): Set CONSTRUCTOR_NO_CLEARING on a constexpr
	constructor that doesn't initialize all the members.
	* decl.c (check_for_uninitialized_const_var): Permit trivial default
	initialization in constexpr.
	* method.c (walk_field_subobs): Still consider a constructor that
	doesn't initialize all the members constexpr.

	* g++.dg/cpp0x/constexpr-array6.C: Adjust dg-error.
	* g++.dg/cpp0x/constexpr-ctor.C: Likewise.
	* g++.dg/cpp0x/constexpr-diag3.C: Likewise.
	* g++.dg/cpp0x/constexpr-diag4.C: Likewise.
	* g++.dg/cpp0x/constexpr-ex3.C: Likewise.
	* g++.dg/cpp0x/constexpr-template2.C: Likewise.
	* g++.dg/cpp0x/constexpr-union2.C: Likewise.
	* g++.dg/cpp0x/lambda/lambda-mangle.C: Rip out a piece of code ...
	* g++.dg/cpp0x/lambda/lambda-mangle6.C: ... and put it here.
	* g++.dg/cpp0x/pr79118.C: Adjust dg-error.
	* g++.dg/cpp1y/constexpr-83921-3.C: Likewise.
	* g++.dg/cpp1y/constexpr-neg1.C: Likewise.
	* g++.dg/cpp1z/constexpr-lambda12.C: Likewise.
	* g++.dg/cpp1z/feat-cxx1z.C: Use -std=c++17.
	* g++.dg/cpp2a/constexpr-init1.C: New test.
	* g++.dg/cpp2a/constexpr-init2.C: New test.
	* g++.dg/cpp2a/constexpr-init3.C: New test.
	* g++.dg/cpp2a/constexpr-init4.C: New test.
	* g++.dg/cpp2a/constexpr-init5.C: New test.
	* g++.dg/cpp2a/constexpr-init6.C: New test.
	* g++.dg/cpp2a/constexpr-init7.C: New test.
	* g++.dg/cpp2a/constexpr-init8.C: New test.
	* g++.dg/cpp2a/constexpr-init9.C: New test.
	* g++.dg/cpp2a/constexpr-init10.C: New test.
	* g++.dg/cpp2a/constexpr-try5.C: Adjust dg-error.
	* g++.dg/cpp2a/feat-cxx2a.C: Test __cpp_constexpr.
	* g++.dg/cpp2a/lambda-mangle.C: New test.
	* g++.dg/debug/dwarf2/pr44641.C: Skip for c++2a.
	* g++.dg/ext/stmtexpr21.C: Adjust dg-error.

Comments

Jason Merrill Nov. 24, 2019, 5:24 p.m. UTC | #1
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.

> constexpr-init10.C demonstrates that we can now elide a constructor call,
> this is caused by the walk_field_subobs hunk.  I hope that's OK.

So long as the object's vptr is initialized properly, absolutely.  Since 
a has static storage duration, constant zero-initialization plus the 
default constructor fully initialize the object.

Jason
Marek Polacek Nov. 27, 2019, 7:36 p.m. UTC | #2
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     }

> > constexpr-init10.C demonstrates that we can now elide a constructor call,
> > this is caused by the walk_field_subobs hunk.  I hope that's OK.
> 
> So long as the object's vptr is initialized properly, absolutely.  Since a
> has static storage duration, constant zero-initialization plus the default
> constructor fully initialize the object.

Ah, that's comforting to hear, thanks.

--
Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA
Jason Merrill Nov. 27, 2019, 9:47 p.m. UTC | #3
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?

>>> constexpr-init10.C demonstrates that we can now elide a constructor call,
>>> this is caused by the walk_field_subobs hunk.  I hope that's OK.
>>
>> So long as the object's vptr is initialized properly, absolutely.  Since a
>> has static storage duration, constant zero-initialization plus the default
>> constructor fully initialize the object.
> 
> Ah, that's comforting to hear, thanks.
> 
> --
> Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA
>
Marek Polacek Nov. 27, 2019, 11:35 p.m. UTC | #4
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'.

Now we look at each element of the constructor; the first one is
D.2364={.i=12}.  But we hit

2233               if (idx != field)
2234                 return false;

because field is 'a' and idx is D.2364.  Were you thinking of tweaking
reduced_constant_expression_p's behavior in C++20 and dropping the whole
cxx_eval_call_expression hunk?

--
Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA
Jason Merrill Nov. 28, 2019, 3:47 a.m. UTC | #5
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.

So perhaps we want to always clear_no_implicit_zero before c++20, and 
always for c++20 and up?

> Now we look at each element of the constructor; the first one is
> D.2364={.i=12}.  But we hit
> 
> 2233               if (idx != field)
> 2234                 return false;
> 
> because field is 'a' and idx is D.2364.  Were you thinking of tweaking
> reduced_constant_expression_p's behavior in C++20 and dropping the whole
> cxx_eval_call_expression hunk?
> 
> --
> Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA
>
Marek Polacek Nov. 29, 2019, 3:28 a.m. UTC | #6
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.

That's why I opted for the approach in my original patch: in register_constexpr_fundef
we still see all the initializers.

Marek
Jason Merrill Nov. 29, 2019, 4:29 a.m. UTC | #7
On Thu, Nov 28, 2019 at 10:28 PM Marek Polacek <polacek@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
Marek Polacek Dec. 2, 2019, 1:09 a.m. UTC | #8
On Thu, Nov 28, 2019 at 11:29:20PM -0500, Jason Merrill wrote:
> Sounds like reduced_constant_expression_p needs to deal better with empty
> bases.

This got a bit complicated because it also needs to handle unions and
now we also need to heed vptr.  But the following seems to work.

(I'll skip the story about a bogus error I hit when building cmcstl2 and
the need to debug a ~90,000 LOC test, because creduce got stuck reducing
it.)

Bootstrapped/regtested on x86_64-linux, built Boost and cmcstl2.

Ok?

2019-12-01  Marek Polacek  <polacek@redhat.com>
	    Jakub Jelinek  <jakub@redhat.com>

	PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
	* c-cppbuiltin.c (c_cpp_builtins): Adjust the value of __cpp_constexpr.

	* class.c (trivial_default_constructor_is_constexpr): Return true in
	C++20.
	* constexpr.c (cx_check_missing_mem_inits): Allow missing field
	initializers in C++20.
	(cxx_eval_call_expression): Don't clear CONSTRUCTOR_NO_CLEARING for
	constexpr constructors in C++20.
	(reduced_constant_expression_p): Don't set FIELD for union and array
	types.  Adjust calls to next_initializable_field.
	* cp-tree.h (next_initializable_field): Adjust declaration.
	* decl.c (check_for_uninitialized_const_var): Permit trivial default
	initialization in constexpr.
	(next_initializable_field): Add a bool parameter.  Use it.
	* method.c (walk_field_subobs): Still consider a constructor that
	doesn't initialize all the members constexpr.

	* g++.dg/cpp0x/constexpr-array6.C: Adjust dg-error.
	* g++.dg/cpp0x/constexpr-ctor.C: Likewise.
	* g++.dg/cpp0x/constexpr-diag3.C: Likewise.
	* g++.dg/cpp0x/constexpr-diag4.C: Likewise.
	* g++.dg/cpp0x/constexpr-ex3.C: Likewise.
	* g++.dg/cpp0x/constexpr-template2.C: Likewise.
	* g++.dg/cpp0x/constexpr-union2.C: Likewise.
	* g++.dg/cpp0x/lambda/lambda-mangle.C: Rip out a piece of code ...
	* g++.dg/cpp0x/lambda/lambda-mangle6.C: ... and put it here.
	* g++.dg/cpp0x/pr79118.C: Adjust dg-error.
	* g++.dg/cpp1y/constexpr-83921-3.C: Likewise.
	* g++.dg/cpp1y/constexpr-neg1.C: Likewise.
	* g++.dg/cpp1z/constexpr-lambda12.C: Likewise.
	* g++.dg/cpp1z/feat-cxx1z.C: Use -std=c++17.
	* g++.dg/cpp2a/constexpr-init1.C: New test.
	* g++.dg/cpp2a/constexpr-init2.C: New test.
	* g++.dg/cpp2a/constexpr-init3.C: New test.
	* g++.dg/cpp2a/constexpr-init4.C: New test.
	* g++.dg/cpp2a/constexpr-init5.C: New test.
	* g++.dg/cpp2a/constexpr-init6.C: New test.
	* g++.dg/cpp2a/constexpr-init7.C: New test.
	* g++.dg/cpp2a/constexpr-init8.C: New test.
	* g++.dg/cpp2a/constexpr-init9.C: New test.
	* g++.dg/cpp2a/constexpr-init10.C: New test.
	* g++.dg/cpp2a/constexpr-init11.C: New test.
	* g++.dg/cpp2a/constexpr-init12.C: New test.
	* g++.dg/cpp2a/constexpr-try5.C: Adjust dg-error.
	* g++.dg/cpp2a/feat-cxx2a.C: Test __cpp_constexpr.
	* g++.dg/cpp2a/lambda-mangle.C: New test.
	* g++.dg/debug/dwarf2/pr44641.C: Skip for c++2a.
	* g++.dg/ext/stmtexpr21.C: Adjust dg-error.

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index 6491545bc3b..680087cd254 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -975,7 +975,8 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_fold_expressions=201603L");
 	  cpp_define (pfile, "__cpp_nontype_template_args=201411L");
 	  cpp_define (pfile, "__cpp_range_based_for=201603L");
-	  cpp_define (pfile, "__cpp_constexpr=201603L");
+	  if (cxx_dialect <= cxx17)
+	    cpp_define (pfile, "__cpp_constexpr=201603L");
 	  cpp_define (pfile, "__cpp_if_constexpr=201606L");
 	  cpp_define (pfile, "__cpp_capture_star_this=201603L");
 	  cpp_define (pfile, "__cpp_inline_variables=201606L");
@@ -997,6 +998,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_init_captures=201803L");
 	  cpp_define (pfile, "__cpp_generic_lambdas=201707L");
 	  cpp_define (pfile, "__cpp_designated_initializers=201707L");
+	  cpp_define (pfile, "__cpp_constexpr=201907L");
 	  cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
 	  cpp_define (pfile, "__cpp_conditional_explicit=201806L");
 	  cpp_define (pfile, "__cpp_consteval=201811L");
diff --git gcc/cp/class.c gcc/cp/class.c
index f36f75fa0db..d8bb44990b7 100644
--- gcc/cp/class.c
+++ gcc/cp/class.c
@@ -5288,8 +5288,14 @@ trivial_default_constructor_is_constexpr (tree t)
   /* A defaulted trivial default constructor is constexpr
      if there is nothing to initialize.  */
   gcc_assert (!TYPE_HAS_COMPLEX_DFLT (t));
-  /* A class with a vptr doesn't have a trivial default ctor.  */
-  return is_really_empty_class (t, /*ignore_vptr*/true);
+  /* A class with a vptr doesn't have a trivial default ctor.
+     In C++20, a class can have transient uninitialized members, e.g.:
+
+       struct S { int i; constexpr S() = default; };
+
+     should work.  */
+  return (cxx_dialect >= cxx2a
+	  || is_really_empty_class (t, /*ignore_vptr*/true));
 }
 
 /* Returns true iff class T has a constexpr default constructor.  */
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index ee3ccb9691c..fa8dde9e22b 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -779,7 +779,9 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
 
   if (TREE_CODE (ctype) == UNION_TYPE)
     {
-      if (nelts == 0 && next_initializable_field (field))
+      if (cxx_dialect < cxx2a
+	  && nelts == 0
+	  && next_initializable_field (field))
 	{
 	  if (complain)
 	    error ("%<constexpr%> constructor for union %qT must "
@@ -815,13 +817,15 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
 	    continue;
 	  if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
 	    {
-	      /* Recurse to check the anonummous aggregate member.  */
+	      /* Recurse to check the anonymous aggregate member.  */
 	      bad |= cx_check_missing_mem_inits
 		(TREE_TYPE (field), NULL_TREE, complain);
 	      if (bad && !complain)
 		return true;
 	      continue;
 	    }
+	  if (cxx_dialect >= cxx2a)
+	    continue;
 	  ftype = strip_array_types (TREE_TYPE (field));
 	  if (type_has_constexpr_default_constructor (ftype))
 	    {
@@ -2153,15 +2157,27 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	entry->result = result;
     }
 
-  /* The result of a constexpr function must be completely initialized.  */
-  if (TREE_CODE (result) == CONSTRUCTOR)
+  /* The result of a constexpr function must be completely initialized.
+
+     However, in C++20, a constexpr constructor doesn't necessarily have
+     to initialize all the fields, so we don't clear CONSTRUCTOR_NO_CLEARING
+     in order to detect reading an unitialized object in constexpr instead
+     of value-initializing it.  (reduced_constant_expression_p is expected to
+     take care of clearing the flag.)  */
+  if (TREE_CODE (result) == CONSTRUCTOR
+      && (cxx_dialect < cxx2a
+	  || !DECL_CONSTRUCTOR_P (fun)
+	  || !DECL_DECLARED_CONSTEXPR_P (fun)))
     clear_no_implicit_zero (result);
 
   pop_cx_call_context ();
   return result;
 }
 
-/* FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
+/* Return true if T is a valid constant initializer.  If a CONSTRUCTOR
+   initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
+   cleared.
+   FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
 
 bool
 reduced_constant_expression_p (tree t)
@@ -2183,8 +2199,17 @@ reduced_constant_expression_p (tree t)
 	  if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
 	    /* An initialized vector would have a VECTOR_CST.  */
 	    return false;
+	  else if (cxx_dialect >= cxx2a
+		   /* An ARRAY_TYPE doesn't have any TYPE_FIELDS.  */
+		   && (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
+		       /* A union only initializes one member.  */
+		       || TREE_CODE (TREE_TYPE (t)) == UNION_TYPE))
+	    field = NULL_TREE;
 	  else
-	    field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)));
+	    /* In C++20, skip fields that don't actually need initializing,
+	       like empty bases.  */
+	    field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)),
+					      cxx_dialect >= cxx2a);
 	}
       else
 	field = NULL_TREE;
@@ -2198,7 +2223,8 @@ reduced_constant_expression_p (tree t)
 	    {
 	      if (idx != field)
 		return false;
-	      field = next_initializable_field (DECL_CHAIN (field));
+	      field = next_initializable_field (DECL_CHAIN (field),
+						cxx_dialect >= cxx2a);
 	    }
 	}
       if (field)
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 7e810b8ee7b..35f3352bdff 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -6541,7 +6541,7 @@ extern bool is_direct_enum_init			(tree, tree);
 extern void initialize_artificial_var		(tree, vec<constructor_elt, va_gc> *);
 extern tree check_var_type			(tree, tree, location_t);
 extern tree reshape_init                        (tree, tree, tsubst_flags_t);
-extern tree next_initializable_field (tree);
+extern tree next_initializable_field		(tree, bool = false);
 extern tree fndecl_declared_return_type		(tree);
 extern bool undeduced_auto_decl			(tree);
 extern bool require_deduced_type		(tree, tsubst_flags_t = tf_warning_or_error);
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 81d73433547..9feba7d5b54 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5835,8 +5835,12 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
      7.1.6 */
   if (VAR_P (decl)
       && !TYPE_REF_P (type)
-      && (constexpr_context_p
-	  || CP_TYPE_CONST_P (type) || var_in_constexpr_fn (decl))
+      && (CP_TYPE_CONST_P (type)
+	  /* C++20 permits trivial default initialization in constexpr
+	     context (P1331R2).  */
+	  || (cxx_dialect < cxx2a
+	      && (constexpr_context_p
+		  || var_in_constexpr_fn (decl))))
       && !DECL_NONTRIVIALLY_INITIALIZED_P (decl))
     {
       tree field = default_init_uninitialized_part (type);
@@ -5845,7 +5849,7 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
 
       bool show_notes = true;
 
-      if (!constexpr_context_p)
+      if (!constexpr_context_p || cxx_dialect >= cxx2a)
 	{
 	  if (CP_TYPE_CONST_P (type))
 	    {
@@ -5906,16 +5910,22 @@ static tree reshape_init_r (tree, reshape_iter *, bool, tsubst_flags_t);
 /* FIELD is a FIELD_DECL or NULL.  In the former case, the value
    returned is the next FIELD_DECL (possibly FIELD itself) that can be
    initialized.  If there are no more such fields, the return value
-   will be NULL.  */
+   will be NULL.  If SKIP_EMPTY, skips empty classes.  */
 
 tree
-next_initializable_field (tree field)
+next_initializable_field (tree field, bool skip_empty /*=false*/)
 {
   while (field
 	 && (TREE_CODE (field) != FIELD_DECL
 	     || DECL_UNNAMED_BIT_FIELD (field)
 	     || (DECL_ARTIFICIAL (field)
-		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field)))))
+		 /* In C++17, don't skip base class fields.  */
+		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field))
+		 /* In C++20, don't skip vptr fields.  */
+		 && !(cxx_dialect >= cxx2a && DECL_VIRTUAL_P (field)))
+	     || (skip_empty
+		 && is_really_empty_class (TREE_TYPE (field),
+					   /*ignore_vptr=*/false))))
     field = DECL_CHAIN (field);
 
   return field;
diff --git gcc/cp/method.c gcc/cp/method.c
index a707940cacc..d2aed473d77 100644
--- gcc/cp/method.c
+++ gcc/cp/method.c
@@ -1985,10 +1985,12 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname,
 	  if (bad && deleted_p)
 	    *deleted_p = true;
 
-	  /* For an implicitly-defined default constructor to be constexpr,
-	     every member must have a user-provided default constructor or
-	     an explicit initializer.  */
-	  if (constexpr_p && !CLASS_TYPE_P (mem_type)
+	  /* Before C++20, for an implicitly-defined default constructor to
+	     be constexpr, every member must have a user-provided default
+	     constructor or an explicit initializer.  */
+	  if (constexpr_p
+	      && cxx_dialect < cxx2a
+	      && !CLASS_TYPE_P (mem_type)
 	      && TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE)
 	    {
 	      *constexpr_p = false;
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
index 16eacdde440..48dae5d9609 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
@@ -4,7 +4,7 @@
 struct A
 {
   int i;
-  constexpr A() {}		// { dg-error "A::i" }
+  constexpr A() {}		// { dg-error "A::i" "" { target c++17_down } }
 };
 
 struct B
@@ -12,4 +12,5 @@ struct B
   A a;
 };
 
-constexpr B b[] = { {} };	// { dg-error "A::A" }
+constexpr B b[] = { {} };	// { dg-error "A::A" "" { target c++17_down } }
+// { dg-error "is not a constant expression" "" { target c++2a } .-1 }
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
index 55beda7c49f..1d0fa479cbc 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
@@ -3,5 +3,5 @@
 struct A
 {
   int i;
-  constexpr A() { }		// { dg-error "A::i" }
+  constexpr A() { }		// { dg-error "A::i" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
index e54b26c7f6a..1c43569615c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
@@ -48,7 +48,7 @@ struct Def
 {
   int _M_i;			// { dg-message "does not initialize" }
 
-  constexpr Def() = default;	// { dg-error "implicit declaration is not .constexpr." }
+  constexpr Def() = default;	// { dg-error "implicit declaration is not .constexpr." "" { target c++17_down } }
 };
 
 constexpr Def defobj;		// { dg-error "uninitialized" }
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
index 13ca6fa2390..c603bdd1a00 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
@@ -21,5 +21,5 @@ struct A1
 struct B1
 {
     A1 a1;
-    constexpr B1() {} // { dg-error "B1::a1" }
+    constexpr B1() {} // { dg-error "B1::a1" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
index a5893563eec..9d6d5ff587c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
@@ -6,7 +6,7 @@
 struct A
 {
   int i;
-  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" }
+  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" "" { target c++17_down } }
 };
 
 template <class T>
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
index 12a8d42b31f..71eb559a24c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
@@ -3,7 +3,7 @@
 template <class T> struct A
 {
   T t;
-  constexpr A() { }		// { dg-error "::t" }
+  constexpr A() { }		// { dg-error "::t" "" { target c++17_down } }
 };
 
 int main()
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
index 1a5e832ac34..c22ecc99efb 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
@@ -14,5 +14,5 @@ union bar
   int x;
   short y;
 
-  constexpr bar() = default;	// { dg-error "constexpr" }
+  constexpr bar() = default;	// { dg-error "constexpr" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
index 15b8b79ecbc..7894ef3051e 100644
--- gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
+++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
@@ -47,17 +47,6 @@ struct S {
 	 []{return 3;}());
 };
 
-template<typename T> struct R {
-  static int x;
-};
-// "int i;" makes the op() non-constexpr in C++17.
-template<typename T> int R<T>::x = []{int i; return 1;}();
-template int R<int>::x;
-// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
-// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
-// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
-// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
-
 void bar()
 {
   // lambdas in non-vague linkage functions have internal linkage.
diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C
new file mode 100644
index 00000000000..9ec13e79bbf
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C
@@ -0,0 +1,15 @@
+// Test lambda mangling
+// { dg-do compile { target { c++11 && c++17_down } } }
+// { dg-require-weak "" }
+// { dg-options "-fno-inline" }
+
+template<typename T> struct R {
+  static int x;
+};
+// "int i;" makes the op() non-constexpr in C++17.  In C++20, it does not.
+template<typename T> int R<T>::x = []{int i; return 1;}();
+template int R<int>::x;
+// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
+// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
+// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
+// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
diff --git gcc/testsuite/g++.dg/cpp0x/pr79118.C gcc/testsuite/g++.dg/cpp0x/pr79118.C
index 5db22a96dd4..616b51ea29a 100644
--- gcc/testsuite/g++.dg/cpp0x/pr79118.C
+++ gcc/testsuite/g++.dg/cpp0x/pr79118.C
@@ -13,7 +13,7 @@ struct One
   constexpr One () : a(), b() {} // { dg-error "multiple" }
   constexpr One (int) : a() {}
   constexpr One (unsigned) : b () {}
-  constexpr One (void *) {} // { dg-error "exactly one" }
+  constexpr One (void *) {} // { dg-error "exactly one" "" { target c++17_down } }
 };
 
 One a ();
@@ -30,10 +30,10 @@ struct Two
   };
 
   constexpr Two () : a(), b() {}
-  constexpr Two (int) : a() {} // { dg-error "b' must be initialized" }
-  constexpr Two (unsigned) : b () {} // { dg-error "a' must be initialized" }
-  constexpr Two (void *) {} // { dg-error "a' must be initialized" }
-   // { dg-error "b' must be initialized" "" { target *-*-* } .-1 }
+  constexpr Two (int) : a() {} // { dg-error "b' must be initialized" "" { target c++17_down } }
+  constexpr Two (unsigned) : b () {} // { dg-error "a' must be initialized" "" { target c++17_down } }
+  constexpr Two (void *) {} // { dg-error "a' must be initialized" "" { target c++17_down } }
+   // { dg-error "b' must be initialized" "" { target c++17_down } .-1 }
 };
 
 Two e ();
diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
index 4b1ed5c3a87..2f1218693e0 100644
--- gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
@@ -2,4 +2,4 @@
 // { dg-do compile { target c++14 } }
 
 struct Foo { int m; };
-constexpr void test() { Foo f; }  // { dg-error "uninitialized" }
+constexpr void test() { Foo f; }  // { dg-error "uninitialized" "" { target c++17_down } }
diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
index d82dbada1bf..53f0f1f7a2b 100644
--- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
@@ -8,7 +8,7 @@ constexpr int f(int i) {
   goto foo;			// { dg-error "goto" }
  foo:
   asm("foo");			// { dg-error "asm" "" { target c++17_down } }
-  int k;			// { dg-error "uninitialized" }
+  int k;			// { dg-error "uninitialized" "" { target c++17_down } }
   A a;				// { dg-error "non-literal" }
   return i;
 }
diff --git gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
index a59bf497b30..93b53273741 100644
--- gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
@@ -3,7 +3,7 @@
 void f(int i)
 {
   [i]() constexpr {
-    int j;			// { dg-error "uninitialized" }
+    int j;			// { dg-error "uninitialized" "" { target c++17_down } }
     j = i;
     return j;
   }();
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
new file mode 100644
index 00000000000..ab7b89da9e8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
@@ -0,0 +1,99 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+// Test basic use.
+
+struct S {
+  int i;
+  constexpr S(bool b) {
+    if (b)
+      i = 42;
+  }
+};
+constexpr S s1(true);
+constexpr S s2(false); // { dg-error "not a constant expression" }
+
+constexpr int
+fn1 (int x)
+{
+  int a;
+  a = 5;
+  return x + a;
+}
+
+static_assert (fn1 (2) == 7);
+
+constexpr int
+fn2 (int x)
+{
+  const int a; // { dg-error "uninitialized .const a." }
+  constexpr int b; // { dg-error "uninitialized .const b." }
+  return x;
+}
+
+constexpr int
+fn3 (int x)
+{
+  int a; // { dg-message ".int a. is not const" }
+  return x + a; // { dg-error "the value of .a. is not usable in a constant expression" }
+}
+
+constexpr int a = fn3 (5); // { dg-message "in .constexpr. expansion of" }
+
+constexpr int
+fn4 ()
+{
+  struct S { int a = -5; int b; } s;
+  return s.a;
+}
+
+static_assert (fn4 () == -5);
+
+constexpr int
+fn5 ()
+{
+  struct S { int a = 9; int b; } s;
+  return s.b;
+}
+
+constexpr int b = fn5 (); // { dg-error "accessing uninitialized member" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+
+constexpr int
+fn6 ()
+{
+  int a;
+  return 42;
+}
+
+static_assert (fn6 () == 42);
+
+constexpr int
+fn7 (bool b)
+{
+  int a; // { dg-message ".int a. is not const" }
+  if (b)
+    a = 42;
+  return a;
+}
+
+static_assert (fn7 (true) == 42);
+static_assert (fn7 (false) == 42); // { dg-error "non-constant condition|the value of .a. is not usable" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+
+constexpr int
+fn8 (int n)
+{
+  int r;
+  switch (n)
+    {
+    case 1:
+    r = n;
+    return r;
+    case 42:
+    r = n;
+    return r;
+    }
+}
+
+static_assert (fn8 (1) == 1);
+static_assert (fn8 (42) == 42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C
new file mode 100644
index 00000000000..74bf8e6677b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C
@@ -0,0 +1,11 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+// In c++2a we don't emit a call to _ZN3FooI3ArgEC1Ev.
+
+struct Arg;
+struct Base {
+  int i;
+  virtual ~Base();
+};
+template <class> struct Foo : Base { };
+Foo<Arg> a;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C
new file mode 100644
index 00000000000..1c7836a674a
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C
@@ -0,0 +1,16 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int i;
+  constexpr S(int) : i(10) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C
new file mode 100644
index 00000000000..7d3d3729b31
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C
@@ -0,0 +1,16 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int uninit;
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42); // { dg-error "not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C
new file mode 100644
index 00000000000..541da1c023f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C
@@ -0,0 +1,15 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+  int i;
+  constexpr A() : i{} {} 
+};
+
+struct B
+{
+  A a;
+};
+
+constexpr B b[] = { {} };
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C
new file mode 100644
index 00000000000..dd2735289cb
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C
@@ -0,0 +1,16 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+  int i;
+  constexpr A() {} 
+};
+
+struct B
+{
+  A a;
+};
+
+// A::i not initialized.
+constexpr B b[] = { {} }; // { dg-error "is not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C
new file mode 100644
index 00000000000..dd614ede2c6
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C
@@ -0,0 +1,61 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+// This bullet in [dcl.constexpr] is now gone:
+//  - every non-static data member and base class sub-object shall be initialized
+
+struct A {
+  int i;
+  constexpr A(int _i) { i = _i; }
+};
+
+struct B {
+  int i;
+  constexpr B() { }
+};
+
+// Anonymous members.
+struct E {
+  int a;
+  union {
+    char b;
+    __extension__ struct {
+      double c;
+      long d;
+    };  
+    union {
+      char e;
+      void *f; 
+    };  
+  };  
+  __extension__ struct {
+    long long g;
+    __extension__ struct {
+      int h;
+      double i;
+    };  
+    union {
+      char *j; 
+      E *k; 
+    };  
+  };  
+
+  // Completely initialized.
+  constexpr E(int(&)[1]) : a(), b(), g(), h(), i(), j() {}
+  constexpr E(int(&)[3]) : a(), e(), g(), h(), i(), k() {}
+  constexpr E(int(&)[7]) : a(), b(), g(), h(), i(), j() {}
+  constexpr E(int(&)[8]) : a(), f(), g(), h(), i(), k() {}
+  constexpr E(int(&)[9]) : a(), c(), d(), g(), h(), i(), k() {}
+
+  // Missing d, i, j/k union init.
+  constexpr E(int(&)[2]) : a(), c(), g(), h() {}
+
+  // Missing h, j/k union init.
+  constexpr E(int(&)[4]) : a(), c(), d(), g(), i() {}
+
+  // Missing b/c/d/e/f union init.
+  constexpr E(int(&)[5]) : a(), g(), h(), i(), k() {}
+
+  // Missing a, b/c/d/e/f union, g/h/i/j/k struct init.
+  constexpr E(int(&)[6]) {}
+};
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C
new file mode 100644
index 00000000000..0d21f26da0e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C
@@ -0,0 +1,22 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S { int i; };
+
+constexpr void
+fn ()
+{
+  S s;
+
+  []() constexpr {
+    int i;
+  }();
+}
+
+constexpr int
+fn2 ()
+{
+  return __extension__ ({ int n; n; }); // { dg-error "not usable in a constant expression" }
+}
+
+constexpr int i = fn2 (); // { dg-message "in .constexpr. expansion of" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C
new file mode 100644
index 00000000000..a2994f5272c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C
@@ -0,0 +1,26 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+/* We used to get the "constexpr constructor for union S::<unnamed union>
+   must initialize exactly one non-static data member" error, but not anymore
+   in C++20.  */
+
+struct S {
+  union {
+    int i;
+    double d;
+  };
+  constexpr S() { }
+};
+
+union U {
+  int a;
+  constexpr U() { }
+};
+
+struct W {
+  union {
+    int a;
+  };
+  constexpr W() { }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C
new file mode 100644
index 00000000000..dd2741efa8c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C
@@ -0,0 +1,63 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int a = 1;
+  constexpr S() = default;
+};
+
+constexpr S s;
+
+union U {
+  int a = 1;
+  constexpr U() = default;
+};
+
+constexpr U u;
+
+struct S2 {
+  int a;
+  constexpr S2() = default;
+};
+
+constexpr S2 s2; // { dg-error "uninitialized .const s2." }
+
+union U2 {
+  int a;
+  constexpr U2() = default;
+};
+
+constexpr U2 u2; // { dg-error "uninitialized .const u2." }
+
+struct S3 {
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int a;
+  } u;
+  constexpr S3() = default;
+};
+
+constexpr S3 s3; // { dg-error "uninitialized .const s3." }
+
+struct S4 {
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int n;
+  } u;
+  constexpr S4() = default;
+};
+
+constexpr S4 s4; // { dg-error "uninitialized .const s4." }
+
+struct S5 {
+  union {
+    int n = 0;
+  };
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int m;
+  } u;
+  constexpr S5() = default;
+};
+
+constexpr S5 s5; // { dg-error "uninitialized .const s5.|not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C
new file mode 100644
index 00000000000..0d5a4a79c90
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C
@@ -0,0 +1,15 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C
new file mode 100644
index 00000000000..b44098cc89b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C
@@ -0,0 +1,17 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int (&)[8]) : W(8) { }
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+int arr[8];
+constexpr auto a = W(arr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
index 47cdce88e36..3b51bf7c901 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
@@ -4,13 +4,13 @@
 
 constexpr int foo ()
 try {			// { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } }
-  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" }
+  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
   static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
   goto l;		// { dg-error "'goto' in 'constexpr' function" }
   l:;
   return 0;
 } catch (...) {
-  long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" }
+  long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
   static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function" }
   goto l2;		// { dg-error "'goto' in 'constexpr' function" }
   l2:;
@@ -19,19 +19,19 @@ try {			// { dg-warning "function-try-block body of 'constexpr' function only av
 
 constexpr int bar ()
 {
-  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" }
+  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
   static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
   goto l;		// { dg-error "'goto' in 'constexpr' function" }
   l:;
   try {			// { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } }
-    short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" }
+    short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
     static float d;	// { dg-error "'d' declared 'static' in 'constexpr' function" }
-			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target *-*-* } .-1 }
+			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target c++17_down } .-1 }
     goto l2;		// { dg-error "'goto' in 'constexpr' function" }
     l2:;
     return 0;
   } catch (int) {
-    char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" }
+    char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" "" { target c++17_down } }
     static int f = 5;	// { dg-error "'f' declared 'static' in 'constexpr' function" }
     goto l3;		// { dg-error "'goto' in 'constexpr' function" }
     l3:;
diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 9b6e2f59d2c..c5c16b6bcc7 100644
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -134,8 +134,8 @@
 
 #ifndef __cpp_constexpr
 #  error "__cpp_constexpr"
-#elif __cpp_constexpr != 201603
-#  error "__cpp_constexpr != 201603"
+#elif __cpp_constexpr != 201907
+#  error "__cpp_constexpr != 201907"
 #endif
 
 #ifndef __cpp_decltype_auto
diff --git gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C
new file mode 100644
index 00000000000..8ee9b0327a5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C
@@ -0,0 +1,15 @@
+// Test lambda mangling
+// { dg-do compile { target c++2a } }
+// { dg-require-weak "" }
+// { dg-options "-fno-inline" }
+
+template<typename T> struct R {
+  static int x;
+};
+// "int i;" makes the op() non-constexpr in C++17.  In C++20, it does not.
+template<typename T> int R<T>::x = []{int i; return 1;}();
+template int R<int>::x;
+// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
+// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
+// { dg-final { scan-assembler-not "_ZNK1RIiE1xMUlvE_clEv" } }
+// { dg-final { scan-assembler-not "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" } }
diff --git gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
index d50df25f693..1139f412fda 100644
--- gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
+++ gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
@@ -1,5 +1,5 @@
 // Origin: PR 44641
-// { dg-do compile }
+// { dg-do compile { target c++17_down } }
 // { dg-options "-gdwarf-2 -O0 -dA" }
 
 template <class A> struct MisplacedDbg;
@@ -40,3 +40,11 @@ struct MisplacedDbg  // { dg-function-on-line {_ZN12MisplacedDbgI3ArgEC[12]Ev} {
 static MisplacedDbg<Arg> static_var1;
 static MisplacedDbg<Arg*> static_var2;
 static MisplacedDbg<Full> static_var3;
+
+// This test is skipped in C++20 because we consider the default constructor
+// MisplacedDbg() constexpr despite the uninitialized member "int i;".  So
+// the calls to
+//    MisplacedDbg<Arg>::MisplacedDbg()
+//    MisplacedDbg<Full>::MisplacedDbg()
+//    MisplacedDbg<Arg*>::MisplacedDbg()
+// are elided.  (This comment is here not to mess up the line numbers.)
diff --git gcc/testsuite/g++.dg/ext/stmtexpr21.C gcc/testsuite/g++.dg/ext/stmtexpr21.C
index 259cb2f1913..97052a117a7 100644
--- gcc/testsuite/g++.dg/ext/stmtexpr21.C
+++ gcc/testsuite/g++.dg/ext/stmtexpr21.C
@@ -7,7 +7,7 @@ struct test { const int *addr; };
 const test* setup()
 {
   static constexpr test atest =
-    { ({ int inner; (const int*)(0); }) };  // { dg-error "uninitialized" }
+    { ({ int inner; (const int*)(0); }) };  // { dg-error "uninitialized" "" { target c++17_down } }
 
   return &atest;
 }
Jakub Jelinek Dec. 2, 2019, 8:15 a.m. UTC | #9
On Sun, Dec 01, 2019 at 08:09:56PM -0500, Marek Polacek wrote:
> On Thu, Nov 28, 2019 at 11:29:20PM -0500, Jason Merrill wrote:
> > Sounds like reduced_constant_expression_p needs to deal better with empty
> > bases.
> 
> This got a bit complicated because it also needs to handle unions and
> now we also need to heed vptr.  But the following seems to work.
> 
> (I'll skip the story about a bogus error I hit when building cmcstl2 and
> the need to debug a ~90,000 LOC test, because creduce got stuck reducing
> it.)

Note, I got creduce stuck several times and what helped is:
--- /usr/bin/creduce	2019-02-15 17:17:32.000000000 +0100
+++ /usr/bin/creduce.nonnested	2019-11-30 11:34:21.604937392 +0100
@@ -802,7 +802,7 @@ my @all_methods = (
     { "name" => "pass_clang",    "arg" => "local-to-global",        "pri" => 9500, "C" => 1, },
     { "name" => "pass_clang",    "arg" => "param-to-global",        "pri" => 203,  "C" => 1, },
     { "name" => "pass_clang",    "arg" => "param-to-local",         "pri" => 204,  "C" => 1, },
-    { "name" => "pass_clang",    "arg" => "remove-nested-function", "pri" => 205,  "C" => 1, },
+   #{ "name" => "pass_clang",    "arg" => "remove-nested-function", "pri" => 205,  "C" => 1, },
     { "name" => "pass_clang",    "arg" => "rename-fun",                            "last_pass_pri" => 207, "C" => 1, },
     { "name" => "pass_clang",    "arg" => "union-to-struct",        "pri" => 208,  },
     { "name" => "pass_clang",    "arg" => "rename-param",                          "last_pass_pri" => 209, "C" => 1, },
where I can use creduce.nonnested if normal creduce gets stuck.  That pass
tends to increase size of code rather than reduce if there are nested
functions, by adding a large series of temporaries:
  type __reduce_tmp_123 = ...
  type __reduce_tmp_122 = __reduce_tmp_123;
  type __reduce_tmp_121 = __reduce_tmp_122;
  type __reduce_tmp_120 = __reduce_tmp_121;
  type __reduce_tmp_119 = __reduce_tmp_120;
  type __reduce_tmp_118 = __reduce_tmp_119;
  type __reduce_tmp_117 = __reduce_tmp_118;
...
and every iteration adds another line.

	Jakub
Jason Merrill Dec. 2, 2019, 5:09 p.m. UTC | #10
On 12/1/19 8:09 PM, Marek Polacek wrote:
> On Thu, Nov 28, 2019 at 11:29:20PM -0500, Jason Merrill wrote:
>> Sounds like reduced_constant_expression_p needs to deal better with empty
>> bases.
> 
> This got a bit complicated because it also needs to handle unions and
> now we also need to heed vptr.  But the following seems to work.
> 
> (I'll skip the story about a bogus error I hit when building cmcstl2 and
> the need to debug a ~90,000 LOC test, because creduce got stuck reducing
> it.)
> 
> Bootstrapped/regtested on x86_64-linux, built Boost and cmcstl2.
> 
> Ok?
> 
> 2019-12-01  Marek Polacek  <polacek@redhat.com>
> 	    Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> 	* c-cppbuiltin.c (c_cpp_builtins): Adjust the value of __cpp_constexpr.
> 
> 	* class.c (trivial_default_constructor_is_constexpr): Return true in
> 	C++20.
> 	* constexpr.c (cx_check_missing_mem_inits): Allow missing field
> 	initializers in C++20.
> 	(cxx_eval_call_expression): Don't clear CONSTRUCTOR_NO_CLEARING for
> 	constexpr constructors in C++20.
> 	(reduced_constant_expression_p): Don't set FIELD for union and array
> 	types.  Adjust calls to next_initializable_field.
> 	* cp-tree.h (next_initializable_field): Adjust declaration.
> 	* decl.c (check_for_uninitialized_const_var): Permit trivial default
> 	initialization in constexpr.
> 	(next_initializable_field): Add a bool parameter.  Use it.
> 	* method.c (walk_field_subobs): Still consider a constructor that
> 	doesn't initialize all the members constexpr.
> 
> 	* g++.dg/cpp0x/constexpr-array6.C: Adjust dg-error.
> 	* g++.dg/cpp0x/constexpr-ctor.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-diag3.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-diag4.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-ex3.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-template2.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-union2.C: Likewise.
> 	* g++.dg/cpp0x/lambda/lambda-mangle.C: Rip out a piece of code ...
> 	* g++.dg/cpp0x/lambda/lambda-mangle6.C: ... and put it here.
> 	* g++.dg/cpp0x/pr79118.C: Adjust dg-error.
> 	* g++.dg/cpp1y/constexpr-83921-3.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-neg1.C: Likewise.
> 	* g++.dg/cpp1z/constexpr-lambda12.C: Likewise.
> 	* g++.dg/cpp1z/feat-cxx1z.C: Use -std=c++17.
> 	* g++.dg/cpp2a/constexpr-init1.C: New test.
> 	* g++.dg/cpp2a/constexpr-init2.C: New test.
> 	* g++.dg/cpp2a/constexpr-init3.C: New test.
> 	* g++.dg/cpp2a/constexpr-init4.C: New test.
> 	* g++.dg/cpp2a/constexpr-init5.C: New test.
> 	* g++.dg/cpp2a/constexpr-init6.C: New test.
> 	* g++.dg/cpp2a/constexpr-init7.C: New test.
> 	* g++.dg/cpp2a/constexpr-init8.C: New test.
> 	* g++.dg/cpp2a/constexpr-init9.C: New test.
> 	* g++.dg/cpp2a/constexpr-init10.C: New test.
> 	* g++.dg/cpp2a/constexpr-init11.C: New test.
> 	* g++.dg/cpp2a/constexpr-init12.C: New test.
> 	* g++.dg/cpp2a/constexpr-try5.C: Adjust dg-error.
> 	* g++.dg/cpp2a/feat-cxx2a.C: Test __cpp_constexpr.
> 	* g++.dg/cpp2a/lambda-mangle.C: New test.
> 	* g++.dg/debug/dwarf2/pr44641.C: Skip for c++2a.
> 	* g++.dg/ext/stmtexpr21.C: Adjust dg-error.
> 
> diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
> index 6491545bc3b..680087cd254 100644
> --- gcc/c-family/c-cppbuiltin.c
> +++ gcc/c-family/c-cppbuiltin.c
> @@ -975,7 +975,8 @@ c_cpp_builtins (cpp_reader *pfile)
>   	  cpp_define (pfile, "__cpp_fold_expressions=201603L");
>   	  cpp_define (pfile, "__cpp_nontype_template_args=201411L");
>   	  cpp_define (pfile, "__cpp_range_based_for=201603L");
> -	  cpp_define (pfile, "__cpp_constexpr=201603L");
> +	  if (cxx_dialect <= cxx17)
> +	    cpp_define (pfile, "__cpp_constexpr=201603L");
>   	  cpp_define (pfile, "__cpp_if_constexpr=201606L");
>   	  cpp_define (pfile, "__cpp_capture_star_this=201603L");
>   	  cpp_define (pfile, "__cpp_inline_variables=201606L");
> @@ -997,6 +998,7 @@ c_cpp_builtins (cpp_reader *pfile)
>   	  cpp_define (pfile, "__cpp_init_captures=201803L");
>   	  cpp_define (pfile, "__cpp_generic_lambdas=201707L");
>   	  cpp_define (pfile, "__cpp_designated_initializers=201707L");
> +	  cpp_define (pfile, "__cpp_constexpr=201907L");
>   	  cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
>   	  cpp_define (pfile, "__cpp_conditional_explicit=201806L");
>   	  cpp_define (pfile, "__cpp_consteval=201811L");
> diff --git gcc/cp/class.c gcc/cp/class.c
> index f36f75fa0db..d8bb44990b7 100644
> --- gcc/cp/class.c
> +++ gcc/cp/class.c
> @@ -5288,8 +5288,14 @@ trivial_default_constructor_is_constexpr (tree t)
>     /* A defaulted trivial default constructor is constexpr
>        if there is nothing to initialize.  */
>     gcc_assert (!TYPE_HAS_COMPLEX_DFLT (t));
> -  /* A class with a vptr doesn't have a trivial default ctor.  */
> -  return is_really_empty_class (t, /*ignore_vptr*/true);
> +  /* A class with a vptr doesn't have a trivial default ctor.
> +     In C++20, a class can have transient uninitialized members, e.g.:
> +
> +       struct S { int i; constexpr S() = default; };
> +
> +     should work.  */
> +  return (cxx_dialect >= cxx2a
> +	  || is_really_empty_class (t, /*ignore_vptr*/true));
>   }
>   
>   /* Returns true iff class T has a constexpr default constructor.  */
> diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
> index ee3ccb9691c..fa8dde9e22b 100644
> --- gcc/cp/constexpr.c
> +++ gcc/cp/constexpr.c
> @@ -779,7 +779,9 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
>   
>     if (TREE_CODE (ctype) == UNION_TYPE)
>       {
> -      if (nelts == 0 && next_initializable_field (field))
> +      if (cxx_dialect < cxx2a
> +	  && nelts == 0
> +	  && next_initializable_field (field))

Do we want cx_check_missing_mem_inits to do anything in C++20?  Or can 
we return immediately at the beginning?

>   	{
>   	  if (complain)
>   	    error ("%<constexpr%> constructor for union %qT must "
> @@ -2153,15 +2157,27 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
>   	entry->result = result;
>       }
>   
> -  /* The result of a constexpr function must be completely initialized.  */
> -  if (TREE_CODE (result) == CONSTRUCTOR)
> +  /* The result of a constexpr function must be completely initialized.
> +
> +     However, in C++20, a constexpr constructor doesn't necessarily have
> +     to initialize all the fields, so we don't clear CONSTRUCTOR_NO_CLEARING
> +     in order to detect reading an unitialized object in constexpr instead
> +     of value-initializing it.  (reduced_constant_expression_p is expected to
> +     take care of clearing the flag.)  */
> +  if (TREE_CODE (result) == CONSTRUCTOR
> +      && (cxx_dialect < cxx2a
> +	  || !DECL_CONSTRUCTOR_P (fun)
> +	  || !DECL_DECLARED_CONSTEXPR_P (fun)))

How can we get here for a non-constexpr function?

>       clear_no_implicit_zero (result);
>   
>     pop_cx_call_context ();
>     return result;
>   }
>   
> -/* FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
> +/* Return true if T is a valid constant initializer.  If a CONSTRUCTOR
> +   initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
> +   cleared.
> +   FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
>   
>   bool
>   reduced_constant_expression_p (tree t)
> @@ -2183,8 +2199,17 @@ reduced_constant_expression_p (tree t)
>   	  if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
>   	    /* An initialized vector would have a VECTOR_CST.  */
>   	    return false;
> +	  else if (cxx_dialect >= cxx2a
> +		   /* An ARRAY_TYPE doesn't have any TYPE_FIELDS.  */
> +		   && (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
> +		       /* A union only initializes one member.  */
> +		       || TREE_CODE (TREE_TYPE (t)) == UNION_TYPE))
> +	    field = NULL_TREE;
>   	  else
> -	    field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)));
> +	    /* In C++20, skip fields that don't actually need initializing,
> +	       like empty bases.  */
> +	    field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)),
> +					      cxx_dialect >= cxx2a);
>   	}
>         else
>   	field = NULL_TREE;
> @@ -2198,7 +2223,8 @@ reduced_constant_expression_p (tree t)
>   	    {
>   	      if (idx != field)
>   		return false;
> -	      field = next_initializable_field (DECL_CHAIN (field));
> +	      field = next_initializable_field (DECL_CHAIN (field),
> +						cxx_dialect >= cxx2a);
>   	    }
>   	}
>         if (field)
> diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
> index 7e810b8ee7b..35f3352bdff 100644
> --- gcc/cp/cp-tree.h
> +++ gcc/cp/cp-tree.h
> @@ -6541,7 +6541,7 @@ extern bool is_direct_enum_init			(tree, tree);
>   extern void initialize_artificial_var		(tree, vec<constructor_elt, va_gc> *);
>   extern tree check_var_type			(tree, tree, location_t);
>   extern tree reshape_init                        (tree, tree, tsubst_flags_t);
> -extern tree next_initializable_field (tree);
> +extern tree next_initializable_field		(tree, bool = false);
>   extern tree fndecl_declared_return_type		(tree);
>   extern bool undeduced_auto_decl			(tree);
>   extern bool require_deduced_type		(tree, tsubst_flags_t = tf_warning_or_error);
> diff --git gcc/cp/decl.c gcc/cp/decl.c
> index 81d73433547..9feba7d5b54 100644
> --- gcc/cp/decl.c
> +++ gcc/cp/decl.c
> +next_initializable_field (tree field, bool skip_empty /*=false*/)
>   {
>     while (field
>   	 && (TREE_CODE (field) != FIELD_DECL
>   	     || DECL_UNNAMED_BIT_FIELD (field)
>   	     || (DECL_ARTIFICIAL (field)
> -		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field)))))
> +		 /* In C++17, don't skip base class fields.  */
> +		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field))
> +		 /* In C++20, don't skip vptr fields.  */
> +		 && !(cxx_dialect >= cxx2a && DECL_VIRTUAL_P (field)))

I don't think this needs to be specific to C++20; in all language 
revisions a vptr makes a class non-aggregate, so we'd only get here for 
a use like that in reduced_constant_expression_p.

> +	     || (skip_empty
> +		 && is_really_empty_class (TREE_TYPE (field),

This should probably check DECL_SIZE (field) == size_zero_node instead, 
since that will properly distinguish between overlapping and 
non-overlapping data members of empty class type.  And please test how 
this works with data members of empty class type both with and without 
[[no_unique_address]].

Jason
Marek Polacek Dec. 2, 2019, 10:09 p.m. UTC | #11
On Mon, Dec 02, 2019 at 12:09:17PM -0500, Jason Merrill wrote:
> On 12/1/19 8:09 PM, Marek Polacek wrote:
> > --- gcc/cp/constexpr.c
> > +++ gcc/cp/constexpr.c
> > @@ -779,7 +779,9 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
> >     if (TREE_CODE (ctype) == UNION_TYPE)
> >       {
> > -      if (nelts == 0 && next_initializable_field (field))
> > +      if (cxx_dialect < cxx2a
> > +	  && nelts == 0
> > +	  && next_initializable_field (field))
> 
> Do we want cx_check_missing_mem_inits to do anything in C++20?  Or can we
> return immediately at the beginning?

We can just return right away.  Changed.

> >   	{
> >   	  if (complain)
> >   	    error ("%<constexpr%> constructor for union %qT must "
> > @@ -2153,15 +2157,27 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
> >   	entry->result = result;
> >       }
> > -  /* The result of a constexpr function must be completely initialized.  */
> > -  if (TREE_CODE (result) == CONSTRUCTOR)
> > +  /* The result of a constexpr function must be completely initialized.
> > +
> > +     However, in C++20, a constexpr constructor doesn't necessarily have
> > +     to initialize all the fields, so we don't clear CONSTRUCTOR_NO_CLEARING
> > +     in order to detect reading an unitialized object in constexpr instead
> > +     of value-initializing it.  (reduced_constant_expression_p is expected to
> > +     take care of clearing the flag.)  */
> > +  if (TREE_CODE (result) == CONSTRUCTOR
> > +      && (cxx_dialect < cxx2a
> > +	  || !DECL_CONSTRUCTOR_P (fun)
> > +	  || !DECL_DECLARED_CONSTEXPR_P (fun)))
> 
> How can we get here for a non-constexpr function?

We can't -- I was just being explicit.  I dropped the DECL_DECLARED_CONSTEXPR_P
line.

> >       clear_no_implicit_zero (result);
> >     pop_cx_call_context ();
> >     return result;
> >   }
> > -/* FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
> > +/* Return true if T is a valid constant initializer.  If a CONSTRUCTOR
> > +   initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
> > +   cleared.
> > +   FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
> >   bool
> >   reduced_constant_expression_p (tree t)
> > @@ -2183,8 +2199,17 @@ reduced_constant_expression_p (tree t)
> >   	  if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
> >   	    /* An initialized vector would have a VECTOR_CST.  */
> >   	    return false;
> > +	  else if (cxx_dialect >= cxx2a
> > +		   /* An ARRAY_TYPE doesn't have any TYPE_FIELDS.  */
> > +		   && (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
> > +		       /* A union only initializes one member.  */
> > +		       || TREE_CODE (TREE_TYPE (t)) == UNION_TYPE))
> > +	    field = NULL_TREE;
> >   	  else
> > -	    field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)));
> > +	    /* In C++20, skip fields that don't actually need initializing,
> > +	       like empty bases.  */
> > +	    field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)),
> > +					      cxx_dialect >= cxx2a);
> >   	}
> >         else
> >   	field = NULL_TREE;
> > @@ -2198,7 +2223,8 @@ reduced_constant_expression_p (tree t)
> >   	    {
> >   	      if (idx != field)
> >   		return false;
> > -	      field = next_initializable_field (DECL_CHAIN (field));
> > +	      field = next_initializable_field (DECL_CHAIN (field),
> > +						cxx_dialect >= cxx2a);
> >   	    }
> >   	}
> >         if (field)
> > diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
> > index 7e810b8ee7b..35f3352bdff 100644
> > --- gcc/cp/cp-tree.h
> > +++ gcc/cp/cp-tree.h
> > @@ -6541,7 +6541,7 @@ extern bool is_direct_enum_init			(tree, tree);
> >   extern void initialize_artificial_var		(tree, vec<constructor_elt, va_gc> *);
> >   extern tree check_var_type			(tree, tree, location_t);
> >   extern tree reshape_init                        (tree, tree, tsubst_flags_t);
> > -extern tree next_initializable_field (tree);
> > +extern tree next_initializable_field		(tree, bool = false);
> >   extern tree fndecl_declared_return_type		(tree);
> >   extern bool undeduced_auto_decl			(tree);
> >   extern bool require_deduced_type		(tree, tsubst_flags_t = tf_warning_or_error);
> > diff --git gcc/cp/decl.c gcc/cp/decl.c
> > index 81d73433547..9feba7d5b54 100644
> > --- gcc/cp/decl.c
> > +++ gcc/cp/decl.c
> > +next_initializable_field (tree field, bool skip_empty /*=false*/)
> >   {
> >     while (field
> >   	 && (TREE_CODE (field) != FIELD_DECL
> >   	     || DECL_UNNAMED_BIT_FIELD (field)
> >   	     || (DECL_ARTIFICIAL (field)
> > -		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field)))))
> > +		 /* In C++17, don't skip base class fields.  */
> > +		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field))
> > +		 /* In C++20, don't skip vptr fields.  */
> > +		 && !(cxx_dialect >= cxx2a && DECL_VIRTUAL_P (field)))
> 
> I don't think this needs to be specific to C++20; in all language revisions
> a vptr makes a class non-aggregate, so we'd only get here for a use like
> that in reduced_constant_expression_p.

True enough, adjusted.

> > +	     || (skip_empty
> > +		 && is_really_empty_class (TREE_TYPE (field),
> 
> This should probably check DECL_SIZE (field) == size_zero_node instead,
> since that will properly distinguish between overlapping and non-overlapping
> data members of empty class type.  And please test how this works with data
> members of empty class type both with and without [[no_unique_address]].

I don't think that's possible -- empty classes in C++ have sizeof(char), unless
their only member is char[0], then their DECL_SIZE is 0.

I've added two testcases: constexpr-init13.C and constexpr-init14.C.  Is there
another scenario regarding [[no_unique_address]] that you want me to test?

Bootstrapped/regtested on x86_64-linux, built Boost and cmcstl2.

2019-12-02  Marek Polacek  <polacek@redhat.com>
	    Jakub Jelinek  <jakub@redhat.com>

	PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
	* c-cppbuiltin.c (c_cpp_builtins): Adjust the value of __cpp_constexpr.

	* class.c (trivial_default_constructor_is_constexpr): Return true in
	C++20.
	* constexpr.c (cx_check_missing_mem_inits): Allow missing field
	initializers in C++20.
	(cxx_eval_call_expression): Don't clear CONSTRUCTOR_NO_CLEARING for
	constexpr constructors in C++20.
	(reduced_constant_expression_p): Don't set FIELD for union and array
	types.  Adjust calls to next_initializable_field.
	* cp-tree.h (next_initializable_field): Adjust declaration.
	* decl.c (check_for_uninitialized_const_var): Permit trivial default
	initialization in constexpr.
	(next_initializable_field): Add a bool parameter.  Use it.
	* method.c (walk_field_subobs): Still consider a constructor that
	doesn't initialize all the members constexpr.

	* g++.dg/cpp0x/constexpr-array6.C: Adjust dg-error.
	* g++.dg/cpp0x/constexpr-ctor.C: Likewise.
	* g++.dg/cpp0x/constexpr-diag3.C: Likewise.
	* g++.dg/cpp0x/constexpr-diag4.C: Likewise.
	* g++.dg/cpp0x/constexpr-ex3.C: Likewise.
	* g++.dg/cpp0x/constexpr-template2.C: Likewise.
	* g++.dg/cpp0x/constexpr-union2.C: Likewise.
	* g++.dg/cpp0x/lambda/lambda-mangle.C: Rip out a piece of code ...
	* g++.dg/cpp0x/lambda/lambda-mangle6.C: ... and put it here.
	* g++.dg/cpp0x/pr79118.C: Adjust dg-error.
	* g++.dg/cpp1y/constexpr-83921-3.C: Likewise.
	* g++.dg/cpp1y/constexpr-neg1.C: Likewise.
	* g++.dg/cpp1z/constexpr-lambda12.C: Likewise.
	* g++.dg/cpp1z/feat-cxx1z.C: Use -std=c++17.
	* g++.dg/cpp2a/constexpr-init1.C: New test.
	* g++.dg/cpp2a/constexpr-init2.C: New test.
	* g++.dg/cpp2a/constexpr-init3.C: New test.
	* g++.dg/cpp2a/constexpr-init4.C: New test.
	* g++.dg/cpp2a/constexpr-init5.C: New test.
	* g++.dg/cpp2a/constexpr-init6.C: New test.
	* g++.dg/cpp2a/constexpr-init7.C: New test.
	* g++.dg/cpp2a/constexpr-init8.C: New test.
	* g++.dg/cpp2a/constexpr-init9.C: New test.
	* g++.dg/cpp2a/constexpr-init10.C: New test.
	* g++.dg/cpp2a/constexpr-init11.C: New test.
	* g++.dg/cpp2a/constexpr-init12.C: New test.
	* g++.dg/cpp2a/constexpr-init13.C: New test.
	* g++.dg/cpp2a/constexpr-init14.C: New test.
	* g++.dg/cpp2a/constexpr-try5.C: Adjust dg-error.
	* g++.dg/cpp2a/feat-cxx2a.C: Test __cpp_constexpr.
	* g++.dg/cpp2a/lambda-mangle.C: New test.
	* g++.dg/debug/dwarf2/pr44641.C: Skip for c++2a.
	* g++.dg/ext/stmtexpr21.C: Adjust dg-error.

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index 6491545bc3b..680087cd254 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -975,7 +975,8 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_fold_expressions=201603L");
 	  cpp_define (pfile, "__cpp_nontype_template_args=201411L");
 	  cpp_define (pfile, "__cpp_range_based_for=201603L");
-	  cpp_define (pfile, "__cpp_constexpr=201603L");
+	  if (cxx_dialect <= cxx17)
+	    cpp_define (pfile, "__cpp_constexpr=201603L");
 	  cpp_define (pfile, "__cpp_if_constexpr=201606L");
 	  cpp_define (pfile, "__cpp_capture_star_this=201603L");
 	  cpp_define (pfile, "__cpp_inline_variables=201606L");
@@ -997,6 +998,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_init_captures=201803L");
 	  cpp_define (pfile, "__cpp_generic_lambdas=201707L");
 	  cpp_define (pfile, "__cpp_designated_initializers=201707L");
+	  cpp_define (pfile, "__cpp_constexpr=201907L");
 	  cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
 	  cpp_define (pfile, "__cpp_conditional_explicit=201806L");
 	  cpp_define (pfile, "__cpp_consteval=201811L");
diff --git gcc/cp/class.c gcc/cp/class.c
index f36f75fa0db..d8bb44990b7 100644
--- gcc/cp/class.c
+++ gcc/cp/class.c
@@ -5288,8 +5288,14 @@ trivial_default_constructor_is_constexpr (tree t)
   /* A defaulted trivial default constructor is constexpr
      if there is nothing to initialize.  */
   gcc_assert (!TYPE_HAS_COMPLEX_DFLT (t));
-  /* A class with a vptr doesn't have a trivial default ctor.  */
-  return is_really_empty_class (t, /*ignore_vptr*/true);
+  /* A class with a vptr doesn't have a trivial default ctor.
+     In C++20, a class can have transient uninitialized members, e.g.:
+
+       struct S { int i; constexpr S() = default; };
+
+     should work.  */
+  return (cxx_dialect >= cxx2a
+	  || is_really_empty_class (t, /*ignore_vptr*/true));
 }
 
 /* Returns true iff class T has a constexpr default constructor.  */
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index ee3ccb9691c..8952d09f19f 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -767,6 +767,10 @@ massage_constexpr_body (tree fun, tree body)
 static bool
 cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
 {
+  /* We allow uninitialized bases/fields in C++20.  */
+  if (cxx_dialect >= cxx2a)
+    return false;
+
   unsigned nelts = 0;
   
   if (body)
@@ -815,7 +819,7 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
 	    continue;
 	  if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
 	    {
-	      /* Recurse to check the anonummous aggregate member.  */
+	      /* Recurse to check the anonymous aggregate member.  */
 	      bad |= cx_check_missing_mem_inits
 		(TREE_TYPE (field), NULL_TREE, complain);
 	      if (bad && !complain)
@@ -2153,15 +2157,26 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	entry->result = result;
     }
 
-  /* The result of a constexpr function must be completely initialized.  */
-  if (TREE_CODE (result) == CONSTRUCTOR)
+  /* The result of a constexpr function must be completely initialized.
+
+     However, in C++20, a constexpr constructor doesn't necessarily have
+     to initialize all the fields, so we don't clear CONSTRUCTOR_NO_CLEARING
+     in order to detect reading an unitialized object in constexpr instead
+     of value-initializing it.  (reduced_constant_expression_p is expected to
+     take care of clearing the flag.)  */
+  if (TREE_CODE (result) == CONSTRUCTOR
+      && (cxx_dialect < cxx2a
+	  || !DECL_CONSTRUCTOR_P (fun)))
     clear_no_implicit_zero (result);
 
   pop_cx_call_context ();
   return result;
 }
 
-/* FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
+/* Return true if T is a valid constant initializer.  If a CONSTRUCTOR
+   initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
+   cleared.
+   FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
 
 bool
 reduced_constant_expression_p (tree t)
@@ -2183,8 +2198,17 @@ reduced_constant_expression_p (tree t)
 	  if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
 	    /* An initialized vector would have a VECTOR_CST.  */
 	    return false;
+	  else if (cxx_dialect >= cxx2a
+		   /* An ARRAY_TYPE doesn't have any TYPE_FIELDS.  */
+		   && (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
+		       /* A union only initializes one member.  */
+		       || TREE_CODE (TREE_TYPE (t)) == UNION_TYPE))
+	    field = NULL_TREE;
 	  else
-	    field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)));
+	    /* In C++20, skip fields that don't actually need initializing,
+	       like empty bases.  */
+	    field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)),
+					      cxx_dialect >= cxx2a);
 	}
       else
 	field = NULL_TREE;
@@ -2198,7 +2222,8 @@ reduced_constant_expression_p (tree t)
 	    {
 	      if (idx != field)
 		return false;
-	      field = next_initializable_field (DECL_CHAIN (field));
+	      field = next_initializable_field (DECL_CHAIN (field),
+						cxx_dialect >= cxx2a);
 	    }
 	}
       if (field)
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 7e810b8ee7b..35f3352bdff 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -6541,7 +6541,7 @@ extern bool is_direct_enum_init			(tree, tree);
 extern void initialize_artificial_var		(tree, vec<constructor_elt, va_gc> *);
 extern tree check_var_type			(tree, tree, location_t);
 extern tree reshape_init                        (tree, tree, tsubst_flags_t);
-extern tree next_initializable_field (tree);
+extern tree next_initializable_field		(tree, bool = false);
 extern tree fndecl_declared_return_type		(tree);
 extern bool undeduced_auto_decl			(tree);
 extern bool require_deduced_type		(tree, tsubst_flags_t = tf_warning_or_error);
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 81d73433547..0ee68d3bf27 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5835,8 +5835,12 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
      7.1.6 */
   if (VAR_P (decl)
       && !TYPE_REF_P (type)
-      && (constexpr_context_p
-	  || CP_TYPE_CONST_P (type) || var_in_constexpr_fn (decl))
+      && (CP_TYPE_CONST_P (type)
+	  /* C++20 permits trivial default initialization in constexpr
+	     context (P1331R2).  */
+	  || (cxx_dialect < cxx2a
+	      && (constexpr_context_p
+		  || var_in_constexpr_fn (decl))))
       && !DECL_NONTRIVIALLY_INITIALIZED_P (decl))
     {
       tree field = default_init_uninitialized_part (type);
@@ -5845,7 +5849,7 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
 
       bool show_notes = true;
 
-      if (!constexpr_context_p)
+      if (!constexpr_context_p || cxx_dialect >= cxx2a)
 	{
 	  if (CP_TYPE_CONST_P (type))
 	    {
@@ -5906,16 +5910,23 @@ static tree reshape_init_r (tree, reshape_iter *, bool, tsubst_flags_t);
 /* FIELD is a FIELD_DECL or NULL.  In the former case, the value
    returned is the next FIELD_DECL (possibly FIELD itself) that can be
    initialized.  If there are no more such fields, the return value
-   will be NULL.  */
+   will be NULL.  If SKIP_EMPTY, skips empty classes.  */
 
 tree
-next_initializable_field (tree field)
+next_initializable_field (tree field, bool skip_empty /*=false*/)
 {
   while (field
 	 && (TREE_CODE (field) != FIELD_DECL
 	     || DECL_UNNAMED_BIT_FIELD (field)
 	     || (DECL_ARTIFICIAL (field)
-		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field)))))
+		 /* In C++17, don't skip base class fields.  */
+		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field))
+		 /* Don't skip vptr fields.  We might see them when we're
+		    called from reduced_constant_expression_p.  */
+		 && !DECL_VIRTUAL_P (field))
+	     || (skip_empty
+		 && is_really_empty_class (TREE_TYPE (field),
+					   /*ignore_vptr=*/false))))
     field = DECL_CHAIN (field);
 
   return field;
diff --git gcc/cp/method.c gcc/cp/method.c
index a707940cacc..d2aed473d77 100644
--- gcc/cp/method.c
+++ gcc/cp/method.c
@@ -1985,10 +1985,12 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname,
 	  if (bad && deleted_p)
 	    *deleted_p = true;
 
-	  /* For an implicitly-defined default constructor to be constexpr,
-	     every member must have a user-provided default constructor or
-	     an explicit initializer.  */
-	  if (constexpr_p && !CLASS_TYPE_P (mem_type)
+	  /* Before C++20, for an implicitly-defined default constructor to
+	     be constexpr, every member must have a user-provided default
+	     constructor or an explicit initializer.  */
+	  if (constexpr_p
+	      && cxx_dialect < cxx2a
+	      && !CLASS_TYPE_P (mem_type)
 	      && TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE)
 	    {
 	      *constexpr_p = false;
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
index 16eacdde440..48dae5d9609 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
@@ -4,7 +4,7 @@
 struct A
 {
   int i;
-  constexpr A() {}		// { dg-error "A::i" }
+  constexpr A() {}		// { dg-error "A::i" "" { target c++17_down } }
 };
 
 struct B
@@ -12,4 +12,5 @@ struct B
   A a;
 };
 
-constexpr B b[] = { {} };	// { dg-error "A::A" }
+constexpr B b[] = { {} };	// { dg-error "A::A" "" { target c++17_down } }
+// { dg-error "is not a constant expression" "" { target c++2a } .-1 }
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
index 55beda7c49f..1d0fa479cbc 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
@@ -3,5 +3,5 @@
 struct A
 {
   int i;
-  constexpr A() { }		// { dg-error "A::i" }
+  constexpr A() { }		// { dg-error "A::i" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
index e54b26c7f6a..1c43569615c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
@@ -48,7 +48,7 @@ struct Def
 {
   int _M_i;			// { dg-message "does not initialize" }
 
-  constexpr Def() = default;	// { dg-error "implicit declaration is not .constexpr." }
+  constexpr Def() = default;	// { dg-error "implicit declaration is not .constexpr." "" { target c++17_down } }
 };
 
 constexpr Def defobj;		// { dg-error "uninitialized" }
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
index 13ca6fa2390..c603bdd1a00 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
@@ -21,5 +21,5 @@ struct A1
 struct B1
 {
     A1 a1;
-    constexpr B1() {} // { dg-error "B1::a1" }
+    constexpr B1() {} // { dg-error "B1::a1" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
index a5893563eec..9d6d5ff587c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
@@ -6,7 +6,7 @@
 struct A
 {
   int i;
-  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" }
+  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" "" { target c++17_down } }
 };
 
 template <class T>
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
index 12a8d42b31f..71eb559a24c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
@@ -3,7 +3,7 @@
 template <class T> struct A
 {
   T t;
-  constexpr A() { }		// { dg-error "::t" }
+  constexpr A() { }		// { dg-error "::t" "" { target c++17_down } }
 };
 
 int main()
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
index 1a5e832ac34..c22ecc99efb 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
@@ -14,5 +14,5 @@ union bar
   int x;
   short y;
 
-  constexpr bar() = default;	// { dg-error "constexpr" }
+  constexpr bar() = default;	// { dg-error "constexpr" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
index 15b8b79ecbc..7894ef3051e 100644
--- gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
+++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
@@ -47,17 +47,6 @@ struct S {
 	 []{return 3;}());
 };
 
-template<typename T> struct R {
-  static int x;
-};
-// "int i;" makes the op() non-constexpr in C++17.
-template<typename T> int R<T>::x = []{int i; return 1;}();
-template int R<int>::x;
-// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
-// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
-// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
-// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
-
 void bar()
 {
   // lambdas in non-vague linkage functions have internal linkage.
diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C
new file mode 100644
index 00000000000..9ec13e79bbf
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C
@@ -0,0 +1,15 @@
+// Test lambda mangling
+// { dg-do compile { target { c++11 && c++17_down } } }
+// { dg-require-weak "" }
+// { dg-options "-fno-inline" }
+
+template<typename T> struct R {
+  static int x;
+};
+// "int i;" makes the op() non-constexpr in C++17.  In C++20, it does not.
+template<typename T> int R<T>::x = []{int i; return 1;}();
+template int R<int>::x;
+// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
+// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
+// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
+// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
diff --git gcc/testsuite/g++.dg/cpp0x/pr79118.C gcc/testsuite/g++.dg/cpp0x/pr79118.C
index 5db22a96dd4..616b51ea29a 100644
--- gcc/testsuite/g++.dg/cpp0x/pr79118.C
+++ gcc/testsuite/g++.dg/cpp0x/pr79118.C
@@ -13,7 +13,7 @@ struct One
   constexpr One () : a(), b() {} // { dg-error "multiple" }
   constexpr One (int) : a() {}
   constexpr One (unsigned) : b () {}
-  constexpr One (void *) {} // { dg-error "exactly one" }
+  constexpr One (void *) {} // { dg-error "exactly one" "" { target c++17_down } }
 };
 
 One a ();
@@ -30,10 +30,10 @@ struct Two
   };
 
   constexpr Two () : a(), b() {}
-  constexpr Two (int) : a() {} // { dg-error "b' must be initialized" }
-  constexpr Two (unsigned) : b () {} // { dg-error "a' must be initialized" }
-  constexpr Two (void *) {} // { dg-error "a' must be initialized" }
-   // { dg-error "b' must be initialized" "" { target *-*-* } .-1 }
+  constexpr Two (int) : a() {} // { dg-error "b' must be initialized" "" { target c++17_down } }
+  constexpr Two (unsigned) : b () {} // { dg-error "a' must be initialized" "" { target c++17_down } }
+  constexpr Two (void *) {} // { dg-error "a' must be initialized" "" { target c++17_down } }
+   // { dg-error "b' must be initialized" "" { target c++17_down } .-1 }
 };
 
 Two e ();
diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
index 4b1ed5c3a87..2f1218693e0 100644
--- gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
@@ -2,4 +2,4 @@
 // { dg-do compile { target c++14 } }
 
 struct Foo { int m; };
-constexpr void test() { Foo f; }  // { dg-error "uninitialized" }
+constexpr void test() { Foo f; }  // { dg-error "uninitialized" "" { target c++17_down } }
diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
index d82dbada1bf..53f0f1f7a2b 100644
--- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
@@ -8,7 +8,7 @@ constexpr int f(int i) {
   goto foo;			// { dg-error "goto" }
  foo:
   asm("foo");			// { dg-error "asm" "" { target c++17_down } }
-  int k;			// { dg-error "uninitialized" }
+  int k;			// { dg-error "uninitialized" "" { target c++17_down } }
   A a;				// { dg-error "non-literal" }
   return i;
 }
diff --git gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
index a59bf497b30..93b53273741 100644
--- gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
@@ -3,7 +3,7 @@
 void f(int i)
 {
   [i]() constexpr {
-    int j;			// { dg-error "uninitialized" }
+    int j;			// { dg-error "uninitialized" "" { target c++17_down } }
     j = i;
     return j;
   }();
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
new file mode 100644
index 00000000000..ab7b89da9e8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
@@ -0,0 +1,99 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+// Test basic use.
+
+struct S {
+  int i;
+  constexpr S(bool b) {
+    if (b)
+      i = 42;
+  }
+};
+constexpr S s1(true);
+constexpr S s2(false); // { dg-error "not a constant expression" }
+
+constexpr int
+fn1 (int x)
+{
+  int a;
+  a = 5;
+  return x + a;
+}
+
+static_assert (fn1 (2) == 7);
+
+constexpr int
+fn2 (int x)
+{
+  const int a; // { dg-error "uninitialized .const a." }
+  constexpr int b; // { dg-error "uninitialized .const b." }
+  return x;
+}
+
+constexpr int
+fn3 (int x)
+{
+  int a; // { dg-message ".int a. is not const" }
+  return x + a; // { dg-error "the value of .a. is not usable in a constant expression" }
+}
+
+constexpr int a = fn3 (5); // { dg-message "in .constexpr. expansion of" }
+
+constexpr int
+fn4 ()
+{
+  struct S { int a = -5; int b; } s;
+  return s.a;
+}
+
+static_assert (fn4 () == -5);
+
+constexpr int
+fn5 ()
+{
+  struct S { int a = 9; int b; } s;
+  return s.b;
+}
+
+constexpr int b = fn5 (); // { dg-error "accessing uninitialized member" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+
+constexpr int
+fn6 ()
+{
+  int a;
+  return 42;
+}
+
+static_assert (fn6 () == 42);
+
+constexpr int
+fn7 (bool b)
+{
+  int a; // { dg-message ".int a. is not const" }
+  if (b)
+    a = 42;
+  return a;
+}
+
+static_assert (fn7 (true) == 42);
+static_assert (fn7 (false) == 42); // { dg-error "non-constant condition|the value of .a. is not usable" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+
+constexpr int
+fn8 (int n)
+{
+  int r;
+  switch (n)
+    {
+    case 1:
+    r = n;
+    return r;
+    case 42:
+    r = n;
+    return r;
+    }
+}
+
+static_assert (fn8 (1) == 1);
+static_assert (fn8 (42) == 42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C
new file mode 100644
index 00000000000..74bf8e6677b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C
@@ -0,0 +1,11 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+// In c++2a we don't emit a call to _ZN3FooI3ArgEC1Ev.
+
+struct Arg;
+struct Base {
+  int i;
+  virtual ~Base();
+};
+template <class> struct Foo : Base { };
+Foo<Arg> a;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C
new file mode 100644
index 00000000000..1c7836a674a
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C
@@ -0,0 +1,16 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int i;
+  constexpr S(int) : i(10) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C
new file mode 100644
index 00000000000..7d3d3729b31
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C
@@ -0,0 +1,16 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int uninit;
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42); // { dg-error "not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C
new file mode 100644
index 00000000000..3d4460a0eb8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C
@@ -0,0 +1,37 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct E { };
+
+struct S {
+  E e;
+  constexpr S() {}
+};
+
+constexpr S s;
+constexpr S s2[4];
+
+struct W {
+  [[no_unique_address]] E e1, e2;
+  constexpr W() {}
+};
+
+constexpr W w;
+constexpr W w2[4];
+
+struct Y {
+  [[no_unique_address]] E e;
+  __extension__ char a[0];
+  constexpr Y() {}
+};
+
+constexpr Y y;
+constexpr Y y2[4];
+
+struct Z {
+  [[no_unique_address]] E e;
+  int i;
+  constexpr Z(int n) :i(n) { }
+};
+
+constexpr Z z(42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C
new file mode 100644
index 00000000000..6ab6abf1505
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C
@@ -0,0 +1,28 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct E {
+  constexpr E() = default;
+  constexpr E(int) {}
+};
+
+struct W {
+  [[no_unique_address]] E e;
+  constexpr W(int) : e(8) {}
+};
+
+constexpr W w = W(42);
+
+struct S {
+  E e;
+  constexpr S() : e{} { }
+};
+
+constexpr S s;
+
+struct S2 {
+  [[no_unique_address]] E e;
+  constexpr S2() : e{} { }
+};
+
+constexpr S2 s2;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C
new file mode 100644
index 00000000000..541da1c023f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C
@@ -0,0 +1,15 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+  int i;
+  constexpr A() : i{} {} 
+};
+
+struct B
+{
+  A a;
+};
+
+constexpr B b[] = { {} };
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C
new file mode 100644
index 00000000000..dd2735289cb
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C
@@ -0,0 +1,16 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+  int i;
+  constexpr A() {} 
+};
+
+struct B
+{
+  A a;
+};
+
+// A::i not initialized.
+constexpr B b[] = { {} }; // { dg-error "is not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C
new file mode 100644
index 00000000000..dd614ede2c6
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C
@@ -0,0 +1,61 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+// This bullet in [dcl.constexpr] is now gone:
+//  - every non-static data member and base class sub-object shall be initialized
+
+struct A {
+  int i;
+  constexpr A(int _i) { i = _i; }
+};
+
+struct B {
+  int i;
+  constexpr B() { }
+};
+
+// Anonymous members.
+struct E {
+  int a;
+  union {
+    char b;
+    __extension__ struct {
+      double c;
+      long d;
+    };  
+    union {
+      char e;
+      void *f; 
+    };  
+  };  
+  __extension__ struct {
+    long long g;
+    __extension__ struct {
+      int h;
+      double i;
+    };  
+    union {
+      char *j; 
+      E *k; 
+    };  
+  };  
+
+  // Completely initialized.
+  constexpr E(int(&)[1]) : a(), b(), g(), h(), i(), j() {}
+  constexpr E(int(&)[3]) : a(), e(), g(), h(), i(), k() {}
+  constexpr E(int(&)[7]) : a(), b(), g(), h(), i(), j() {}
+  constexpr E(int(&)[8]) : a(), f(), g(), h(), i(), k() {}
+  constexpr E(int(&)[9]) : a(), c(), d(), g(), h(), i(), k() {}
+
+  // Missing d, i, j/k union init.
+  constexpr E(int(&)[2]) : a(), c(), g(), h() {}
+
+  // Missing h, j/k union init.
+  constexpr E(int(&)[4]) : a(), c(), d(), g(), i() {}
+
+  // Missing b/c/d/e/f union init.
+  constexpr E(int(&)[5]) : a(), g(), h(), i(), k() {}
+
+  // Missing a, b/c/d/e/f union, g/h/i/j/k struct init.
+  constexpr E(int(&)[6]) {}
+};
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C
new file mode 100644
index 00000000000..0d21f26da0e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C
@@ -0,0 +1,22 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S { int i; };
+
+constexpr void
+fn ()
+{
+  S s;
+
+  []() constexpr {
+    int i;
+  }();
+}
+
+constexpr int
+fn2 ()
+{
+  return __extension__ ({ int n; n; }); // { dg-error "not usable in a constant expression" }
+}
+
+constexpr int i = fn2 (); // { dg-message "in .constexpr. expansion of" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C
new file mode 100644
index 00000000000..a2994f5272c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C
@@ -0,0 +1,26 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+/* We used to get the "constexpr constructor for union S::<unnamed union>
+   must initialize exactly one non-static data member" error, but not anymore
+   in C++20.  */
+
+struct S {
+  union {
+    int i;
+    double d;
+  };
+  constexpr S() { }
+};
+
+union U {
+  int a;
+  constexpr U() { }
+};
+
+struct W {
+  union {
+    int a;
+  };
+  constexpr W() { }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C
new file mode 100644
index 00000000000..dd2741efa8c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C
@@ -0,0 +1,63 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int a = 1;
+  constexpr S() = default;
+};
+
+constexpr S s;
+
+union U {
+  int a = 1;
+  constexpr U() = default;
+};
+
+constexpr U u;
+
+struct S2 {
+  int a;
+  constexpr S2() = default;
+};
+
+constexpr S2 s2; // { dg-error "uninitialized .const s2." }
+
+union U2 {
+  int a;
+  constexpr U2() = default;
+};
+
+constexpr U2 u2; // { dg-error "uninitialized .const u2." }
+
+struct S3 {
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int a;
+  } u;
+  constexpr S3() = default;
+};
+
+constexpr S3 s3; // { dg-error "uninitialized .const s3." }
+
+struct S4 {
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int n;
+  } u;
+  constexpr S4() = default;
+};
+
+constexpr S4 s4; // { dg-error "uninitialized .const s4." }
+
+struct S5 {
+  union {
+    int n = 0;
+  };
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int m;
+  } u;
+  constexpr S5() = default;
+};
+
+constexpr S5 s5; // { dg-error "uninitialized .const s5.|not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C
new file mode 100644
index 00000000000..0d5a4a79c90
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C
@@ -0,0 +1,15 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C
new file mode 100644
index 00000000000..b44098cc89b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C
@@ -0,0 +1,17 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int (&)[8]) : W(8) { }
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+int arr[8];
+constexpr auto a = W(arr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
index 47cdce88e36..3b51bf7c901 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
@@ -4,13 +4,13 @@
 
 constexpr int foo ()
 try {			// { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } }
-  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" }
+  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
   static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
   goto l;		// { dg-error "'goto' in 'constexpr' function" }
   l:;
   return 0;
 } catch (...) {
-  long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" }
+  long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
   static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function" }
   goto l2;		// { dg-error "'goto' in 'constexpr' function" }
   l2:;
@@ -19,19 +19,19 @@ try {			// { dg-warning "function-try-block body of 'constexpr' function only av
 
 constexpr int bar ()
 {
-  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" }
+  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
   static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
   goto l;		// { dg-error "'goto' in 'constexpr' function" }
   l:;
   try {			// { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } }
-    short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" }
+    short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
     static float d;	// { dg-error "'d' declared 'static' in 'constexpr' function" }
-			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target *-*-* } .-1 }
+			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target c++17_down } .-1 }
     goto l2;		// { dg-error "'goto' in 'constexpr' function" }
     l2:;
     return 0;
   } catch (int) {
-    char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" }
+    char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" "" { target c++17_down } }
     static int f = 5;	// { dg-error "'f' declared 'static' in 'constexpr' function" }
     goto l3;		// { dg-error "'goto' in 'constexpr' function" }
     l3:;
diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 9b6e2f59d2c..c5c16b6bcc7 100644
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -134,8 +134,8 @@
 
 #ifndef __cpp_constexpr
 #  error "__cpp_constexpr"
-#elif __cpp_constexpr != 201603
-#  error "__cpp_constexpr != 201603"
+#elif __cpp_constexpr != 201907
+#  error "__cpp_constexpr != 201907"
 #endif
 
 #ifndef __cpp_decltype_auto
diff --git gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C
new file mode 100644
index 00000000000..8ee9b0327a5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C
@@ -0,0 +1,15 @@
+// Test lambda mangling
+// { dg-do compile { target c++2a } }
+// { dg-require-weak "" }
+// { dg-options "-fno-inline" }
+
+template<typename T> struct R {
+  static int x;
+};
+// "int i;" makes the op() non-constexpr in C++17.  In C++20, it does not.
+template<typename T> int R<T>::x = []{int i; return 1;}();
+template int R<int>::x;
+// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
+// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
+// { dg-final { scan-assembler-not "_ZNK1RIiE1xMUlvE_clEv" } }
+// { dg-final { scan-assembler-not "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" } }
diff --git gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
index d50df25f693..1139f412fda 100644
--- gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
+++ gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
@@ -1,5 +1,5 @@
 // Origin: PR 44641
-// { dg-do compile }
+// { dg-do compile { target c++17_down } }
 // { dg-options "-gdwarf-2 -O0 -dA" }
 
 template <class A> struct MisplacedDbg;
@@ -40,3 +40,11 @@ struct MisplacedDbg  // { dg-function-on-line {_ZN12MisplacedDbgI3ArgEC[12]Ev} {
 static MisplacedDbg<Arg> static_var1;
 static MisplacedDbg<Arg*> static_var2;
 static MisplacedDbg<Full> static_var3;
+
+// This test is skipped in C++20 because we consider the default constructor
+// MisplacedDbg() constexpr despite the uninitialized member "int i;".  So
+// the calls to
+//    MisplacedDbg<Arg>::MisplacedDbg()
+//    MisplacedDbg<Full>::MisplacedDbg()
+//    MisplacedDbg<Arg*>::MisplacedDbg()
+// are elided.  (This comment is here not to mess up the line numbers.)
diff --git gcc/testsuite/g++.dg/ext/stmtexpr21.C gcc/testsuite/g++.dg/ext/stmtexpr21.C
index 259cb2f1913..97052a117a7 100644
--- gcc/testsuite/g++.dg/ext/stmtexpr21.C
+++ gcc/testsuite/g++.dg/ext/stmtexpr21.C
@@ -7,7 +7,7 @@ struct test { const int *addr; };
 const test* setup()
 {
   static constexpr test atest =
-    { ({ int inner; (const int*)(0); }) };  // { dg-error "uninitialized" }
+    { ({ int inner; (const int*)(0); }) };  // { dg-error "uninitialized" "" { target c++17_down } }
 
   return &atest;
 }
Marek Polacek Dec. 3, 2019, 12:38 a.m. UTC | #12
On Mon, Dec 02, 2019 at 09:15:14AM +0100, Jakub Jelinek wrote:
> On Sun, Dec 01, 2019 at 08:09:56PM -0500, Marek Polacek wrote:
> > On Thu, Nov 28, 2019 at 11:29:20PM -0500, Jason Merrill wrote:
> > > Sounds like reduced_constant_expression_p needs to deal better with empty
> > > bases.
> > 
> > This got a bit complicated because it also needs to handle unions and
> > now we also need to heed vptr.  But the following seems to work.
> > 
> > (I'll skip the story about a bogus error I hit when building cmcstl2 and
> > the need to debug a ~90,000 LOC test, because creduce got stuck reducing
> > it.)
> 
> Note, I got creduce stuck several times and what helped is:
> --- /usr/bin/creduce	2019-02-15 17:17:32.000000000 +0100
> +++ /usr/bin/creduce.nonnested	2019-11-30 11:34:21.604937392 +0100
> @@ -802,7 +802,7 @@ my @all_methods = (
>      { "name" => "pass_clang",    "arg" => "local-to-global",        "pri" => 9500, "C" => 1, },
>      { "name" => "pass_clang",    "arg" => "param-to-global",        "pri" => 203,  "C" => 1, },
>      { "name" => "pass_clang",    "arg" => "param-to-local",         "pri" => 204,  "C" => 1, },
> -    { "name" => "pass_clang",    "arg" => "remove-nested-function", "pri" => 205,  "C" => 1, },
> +   #{ "name" => "pass_clang",    "arg" => "remove-nested-function", "pri" => 205,  "C" => 1, },
>      { "name" => "pass_clang",    "arg" => "rename-fun",                            "last_pass_pri" => 207, "C" => 1, },
>      { "name" => "pass_clang",    "arg" => "union-to-struct",        "pri" => 208,  },
>      { "name" => "pass_clang",    "arg" => "rename-param",                          "last_pass_pri" => 209, "C" => 1, },
> where I can use creduce.nonnested if normal creduce gets stuck.  That pass
> tends to increase size of code rather than reduce if there are nested
> functions, by adding a large series of temporaries:
>   type __reduce_tmp_123 = ...
>   type __reduce_tmp_122 = __reduce_tmp_123;
>   type __reduce_tmp_121 = __reduce_tmp_122;
>   type __reduce_tmp_120 = __reduce_tmp_121;
>   type __reduce_tmp_119 = __reduce_tmp_120;
>   type __reduce_tmp_118 = __reduce_tmp_119;
>   type __reduce_tmp_117 = __reduce_tmp_118;
> ...
> and every iteration adds another line.

Ah, that's useful, thanks!

--
Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA
Jason Merrill Dec. 3, 2019, 7:07 a.m. UTC | #13
On 12/2/19 5:09 PM, Marek Polacek wrote:
> On Mon, Dec 02, 2019 at 12:09:17PM -0500, Jason Merrill wrote:
>> On 12/1/19 8:09 PM, Marek Polacek wrote:
>>> +	     || (skip_empty
>>> +		 && is_really_empty_class (TREE_TYPE (field),
>>
>> This should probably check DECL_SIZE (field) == size_zero_node instead,
>> since that will properly distinguish between overlapping and non-overlapping
>> data members of empty class type.  And please test how this works with data
>> members of empty class type both with and without [[no_unique_address]].
> 
> I don't think that's possible -- empty classes in C++ have sizeof(char), unless
> their only member is char[0], then their DECL_SIZE is 0.

I think you're talking about the TYPE_SIZE of the class, and I'm talking 
about the DECL_SIZE of the FIELD_DECL.

> I've added two testcases: constexpr-init13.C and constexpr-init14.C.  Is there
> another scenario regarding [[no_unique_address]] that you want me to test?

I think the classes with empty base fields need to have another 
initialized field after them to have a chance of tripping

   if (idx != field)

Jason
Marek Polacek Dec. 3, 2019, 5:49 p.m. UTC | #14
On Tue, Dec 03, 2019 at 02:07:02AM -0500, Jason Merrill wrote:
> On 12/2/19 5:09 PM, Marek Polacek wrote:
> > On Mon, Dec 02, 2019 at 12:09:17PM -0500, Jason Merrill wrote:
> > > On 12/1/19 8:09 PM, Marek Polacek wrote:
> > > > +	     || (skip_empty
> > > > +		 && is_really_empty_class (TREE_TYPE (field),
> > > 
> > > This should probably check DECL_SIZE (field) == size_zero_node instead,
> > > since that will properly distinguish between overlapping and non-overlapping
> > > data members of empty class type.  And please test how this works with data
> > > members of empty class type both with and without [[no_unique_address]].
> > 
> > I don't think that's possible -- empty classes in C++ have sizeof(char), unless
> > their only member is char[0], then their DECL_SIZE is 0.
> 
> I think you're talking about the TYPE_SIZE of the class, and I'm talking
> about the DECL_SIZE of the FIELD_DECL.

I'm really looking at the DECL_SIZE:

Breakpoint 5, next_initializable_field (field=<field_decl 0x7fffea90dc78 s>, skip_empty=true)
    at /home/mpolacek/src/gcc/gcc/cp/decl.c:5928
5928			 && is_really_empty_class (TREE_TYPE (field),
(gdb) p field
$1 = <field_decl 0x7fffea90dc78 s>
(gdb) p DECL_SIZE($1)
$2 = <integer_cst 0x7fffea8e0f60>
(gdb) pge
8

This is constexpr-init8.C:

struct S {
  constexpr S(int) {}
};

struct W {
  constexpr W(int) : s(8), p() {}

  S s;
  int *p;
};

constexpr auto a = W(42);

I think that's because layout_class_type has:

 6491       else if (might_overlap && is_empty_class (type))
 6492         layout_empty_base_or_field (rli, field, empty_base_offsets);
 6493       else
 6494         layout_nonempty_base_or_field (rli, field, NULL_TREE,
 6495                                        empty_base_offsets);

and here might_overlap is false because 's' doesn't have [[no_unique_address]].
So we emit the FIELD_DECL with size 8.  Its type CLASSTYPE_SIZE is 0 though.
I don't know but I'd rather not mess with this...

> > I've added two testcases: constexpr-init13.C and constexpr-init14.C.  Is there
> > another scenario regarding [[no_unique_address]] that you want me to test?
> 
> I think the classes with empty base fields need to have another initialized
> field after them to have a chance of tripping
> 
>   if (idx != field)

And sure enough, that's exactly the case for

struct E {
  constexpr E() = default;
  constexpr E(int) {}
};

struct S {
  E e;
  int i;
  constexpr S() : e{} , i(11) { }
};

constexpr S s;


And that's even without [[no_unique_address]].

So it's a valid concern.  It's unclear to me how this should be resolved.

--
Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA
Jason Merrill Dec. 3, 2019, 6:40 p.m. UTC | #15
On 12/3/19 12:49 PM, Marek Polacek wrote:
> On Tue, Dec 03, 2019 at 02:07:02AM -0500, Jason Merrill wrote:
>> On 12/2/19 5:09 PM, Marek Polacek wrote:
>>> On Mon, Dec 02, 2019 at 12:09:17PM -0500, Jason Merrill wrote:
>>>> On 12/1/19 8:09 PM, Marek Polacek wrote:
>>>>> +	     || (skip_empty
>>>>> +		 && is_really_empty_class (TREE_TYPE (field),
>>>>
>>>> This should probably check DECL_SIZE (field) == size_zero_node instead,
>>>> since that will properly distinguish between overlapping and non-overlapping
>>>> data members of empty class type.  And please test how this works with data
>>>> members of empty class type both with and without [[no_unique_address]].
>>>
>>> I don't think that's possible -- empty classes in C++ have sizeof(char), unless
>>> their only member is char[0], then their DECL_SIZE is 0.
>>
>> I think you're talking about the TYPE_SIZE of the class, and I'm talking
>> about the DECL_SIZE of the FIELD_DECL.
> 
> I'm really looking at the DECL_SIZE:
> 
> Breakpoint 5, next_initializable_field (field=<field_decl 0x7fffea90dc78 s>, skip_empty=true)
>      at /home/mpolacek/src/gcc/gcc/cp/decl.c:5928
> 5928			 && is_really_empty_class (TREE_TYPE (field),
> (gdb) p field
> $1 = <field_decl 0x7fffea90dc78 s>
> (gdb) p DECL_SIZE($1)
> $2 = <integer_cst 0x7fffea8e0f60>
> (gdb) pge
> 8
> 
> This is constexpr-init8.C:
> 
> struct S {
>    constexpr S(int) {}
> };
> 
> struct W {
>    constexpr W(int) : s(8), p() {}
> 
>    S s;
>    int *p;
> };
> 
> constexpr auto a = W(42);
> 
> I think that's because layout_class_type has:
> 
>   6491       else if (might_overlap && is_empty_class (type))
>   6492         layout_empty_base_or_field (rli, field, empty_base_offsets);
>   6493       else
>   6494         layout_nonempty_base_or_field (rli, field, NULL_TREE,
>   6495                                        empty_base_offsets);
> 
> and here might_overlap is false because 's' doesn't have [[no_unique_address]].
> So we emit the FIELD_DECL with size 8.

Yes, that's correct.  So I don't think we want to skip 's' in 
next_initializable_field; it should have a normal initializer.

> Its type CLASSTYPE_SIZE is 0 though.
> I don't know but I'd rather not mess with this...

>>> I've added two testcases: constexpr-init13.C and constexpr-init14.C.  Is there
>>> another scenario regarding [[no_unique_address]] that you want me to test?
>>
>> I think the classes with empty base fields need to have another initialized
>> field after them to have a chance of tripping
>>
>>    if (idx != field)
> 
> And sure enough, that's exactly the case for
> 
> struct E {
>    constexpr E() = default;
>    constexpr E(int) {}
> };
> 
> struct S {
>    E e;
>    int i;
>    constexpr S() : e{} , i(11) { }
> };
> 
> constexpr S s;
> 
> 
> And that's even without [[no_unique_address]].
> 
> So it's a valid concern.  It's unclear to me how this should be resolved.

What does the initializer for s look like in this case?

Jason
Jason Merrill Dec. 3, 2019, 6:43 p.m. UTC | #16
On 12/3/19 1:40 PM, Jason Merrill wrote:
> On 12/3/19 12:49 PM, Marek Polacek wrote:
>> On Tue, Dec 03, 2019 at 02:07:02AM -0500, Jason Merrill wrote:
>>> On 12/2/19 5:09 PM, Marek Polacek wrote:
>>>> On Mon, Dec 02, 2019 at 12:09:17PM -0500, Jason Merrill wrote:
>>>>> On 12/1/19 8:09 PM, Marek Polacek wrote:
>>>>>> +         || (skip_empty
>>>>>> +         && is_really_empty_class (TREE_TYPE (field),
>>>>>
>>>>> This should probably check DECL_SIZE (field) == size_zero_node 
>>>>> instead,
>>>>> since that will properly distinguish between overlapping and 
>>>>> non-overlapping
>>>>> data members of empty class type.  And please test how this works 
>>>>> with data
>>>>> members of empty class type both with and without 
>>>>> [[no_unique_address]].
>>>>
>>>> I don't think that's possible -- empty classes in C++ have 
>>>> sizeof(char), unless
>>>> their only member is char[0], then their DECL_SIZE is 0.
>>>
>>> I think you're talking about the TYPE_SIZE of the class, and I'm talking
>>> about the DECL_SIZE of the FIELD_DECL.
>>
>> I'm really looking at the DECL_SIZE:
>>
>> Breakpoint 5, next_initializable_field (field=<field_decl 
>> 0x7fffea90dc78 s>, skip_empty=true)
>>      at /home/mpolacek/src/gcc/gcc/cp/decl.c:5928
>> 5928             && is_really_empty_class (TREE_TYPE (field),
>> (gdb) p field
>> $1 = <field_decl 0x7fffea90dc78 s>
>> (gdb) p DECL_SIZE($1)
>> $2 = <integer_cst 0x7fffea8e0f60>
>> (gdb) pge
>> 8
>>
>> This is constexpr-init8.C:
>>
>> struct S {
>>    constexpr S(int) {}
>> };
>>
>> struct W {
>>    constexpr W(int) : s(8), p() {}
>>
>>    S s;
>>    int *p;
>> };
>>
>> constexpr auto a = W(42);
>>
>> I think that's because layout_class_type has:
>>
>>   6491       else if (might_overlap && is_empty_class (type))
>>   6492         layout_empty_base_or_field (rli, field, 
>> empty_base_offsets);
>>   6493       else
>>   6494         layout_nonempty_base_or_field (rli, field, NULL_TREE,
>>   6495                                        empty_base_offsets);
>>
>> and here might_overlap is false because 's' doesn't have 
>> [[no_unique_address]].
>> So we emit the FIELD_DECL with size 8.
> 
> Yes, that's correct.  So I don't think we want to skip 's' in 
> next_initializable_field; it should have a normal initializer.
> 
>> Its type CLASSTYPE_SIZE is 0 though.
>> I don't know but I'd rather not mess with this...
> 
>>>> I've added two testcases: constexpr-init13.C and 
>>>> constexpr-init14.C.  Is there
>>>> another scenario regarding [[no_unique_address]] that you want me to 
>>>> test?
>>>
>>> I think the classes with empty base fields need to have another 
>>> initialized
>>> field after them to have a chance of tripping
>>>
>>>    if (idx != field)
>>
>> And sure enough, that's exactly the case for
>>
>> struct E {
>>    constexpr E() = default;
>>    constexpr E(int) {}
>> };
>>
>> struct S {
>>    E e;
>>    int i;
>>    constexpr S() : e{} , i(11) { }
>> };
>>
>> constexpr S s;
>>
>>
>> And that's even without [[no_unique_address]].
>>
>> So it's a valid concern.  It's unclear to me how this should be resolved.
> 
> What does the initializer for s look like in this case?

One thought: drop the empty base checking in next_initializable_field, 
and change the test in reduced_constant_expression_p to

if (idx != field && !is_empty_class (TREE_TYPE (field)))

so we handle both having or not having an initializer for empty class 
elements.

Jason
Marek Polacek Dec. 4, 2019, 10:43 p.m. UTC | #17
On Tue, Dec 03, 2019 at 01:43:26PM -0500, Jason Merrill wrote:
> On 12/3/19 1:40 PM, Jason Merrill wrote:
> > On 12/3/19 12:49 PM, Marek Polacek wrote:
> > > On Tue, Dec 03, 2019 at 02:07:02AM -0500, Jason Merrill wrote:
> > > > On 12/2/19 5:09 PM, Marek Polacek wrote:
> > > > > On Mon, Dec 02, 2019 at 12:09:17PM -0500, Jason Merrill wrote:
> > > > > > On 12/1/19 8:09 PM, Marek Polacek wrote:
> > > > > > > +         || (skip_empty
> > > > > > > +         && is_really_empty_class (TREE_TYPE (field),
> > > > > > 
> > > > > > This should probably check DECL_SIZE (field) ==
> > > > > > size_zero_node instead,
> > > > > > since that will properly distinguish between overlapping
> > > > > > and non-overlapping
> > > > > > data members of empty class type.  And please test how
> > > > > > this works with data
> > > > > > members of empty class type both with and without
> > > > > > [[no_unique_address]].
> > > > > 
> > > > > I don't think that's possible -- empty classes in C++ have
> > > > > sizeof(char), unless
> > > > > their only member is char[0], then their DECL_SIZE is 0.
> > > > 
> > > > I think you're talking about the TYPE_SIZE of the class, and I'm talking
> > > > about the DECL_SIZE of the FIELD_DECL.
> > > 
> > > I'm really looking at the DECL_SIZE:
> > > 
> > > Breakpoint 5, next_initializable_field (field=<field_decl
> > > 0x7fffea90dc78 s>, skip_empty=true)
> > >      at /home/mpolacek/src/gcc/gcc/cp/decl.c:5928
> > > 5928             && is_really_empty_class (TREE_TYPE (field),
> > > (gdb) p field
> > > $1 = <field_decl 0x7fffea90dc78 s>
> > > (gdb) p DECL_SIZE($1)
> > > $2 = <integer_cst 0x7fffea8e0f60>
> > > (gdb) pge
> > > 8
> > > 
> > > This is constexpr-init8.C:
> > > 
> > > struct S {
> > >    constexpr S(int) {}
> > > };
> > > 
> > > struct W {
> > >    constexpr W(int) : s(8), p() {}
> > > 
> > >    S s;
> > >    int *p;
> > > };
> > > 
> > > constexpr auto a = W(42);
> > > 
> > > I think that's because layout_class_type has:
> > > 
> > >   6491       else if (might_overlap && is_empty_class (type))
> > >   6492         layout_empty_base_or_field (rli, field,
> > > empty_base_offsets);
> > >   6493       else
> > >   6494         layout_nonempty_base_or_field (rli, field, NULL_TREE,
> > >   6495                                        empty_base_offsets);
> > > 
> > > and here might_overlap is false because 's' doesn't have
> > > [[no_unique_address]].
> > > So we emit the FIELD_DECL with size 8.
> > 
> > Yes, that's correct.  So I don't think we want to skip 's' in
> > next_initializable_field; it should have a normal initializer.
> > 
> > > Its type CLASSTYPE_SIZE is 0 though.
> > > I don't know but I'd rather not mess with this...
> > 
> > > > > I've added two testcases: constexpr-init13.C and
> > > > > constexpr-init14.C.  Is there
> > > > > another scenario regarding [[no_unique_address]] that you
> > > > > want me to test?
> > > > 
> > > > I think the classes with empty base fields need to have another
> > > > initialized
> > > > field after them to have a chance of tripping
> > > > 
> > > >    if (idx != field)
> > > 
> > > And sure enough, that's exactly the case for
> > > 
> > > struct E {
> > >    constexpr E() = default;
> > >    constexpr E(int) {}
> > > };
> > > 
> > > struct S {
> > >    E e;
> > >    int i;
> > >    constexpr S() : e{} , i(11) { }
> > > };
> > > 
> > > constexpr S s;
> > > 
> > > 
> > > And that's even without [[no_unique_address]].
> > > 
> > > So it's a valid concern.  It's unclear to me how this should be resolved.
> > 
> > What does the initializer for s look like in this case?
> 
> One thought: drop the empty base checking in next_initializable_field, and
> change the test in reduced_constant_expression_p to
> 
> if (idx != field && !is_empty_class (TREE_TYPE (field)))
> 
> so we handle both having or not having an initializer for empty class
> elements.

Right, turns out the skipping in next_initializable_field won't fly, as we do
not always want to skip an empty field.

This is a version I'm finally happy with; it's a variation of what you
suggested.  I used is_really_empty_class to also handle empty classes
containing empty classes (as in constexpr-empty11.C), and the goto handling
is needed because sometimes we want to skip a field, but not an initializer.
Perhaps the retry label should be before the if (field) check to not crash
on a null field, but I didn't hit that when testing.

constexpr-init15.C is another test testing [[no_unique_address]].

Bootstrapped/regtested on x86_64-linux, built Boost and cmcstl2.

Ok?

2019-12-04  Marek Polacek  <polacek@redhat.com>
	    Jakub Jelinek  <jakub@redhat.com>

	PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
	* c-cppbuiltin.c (c_cpp_builtins): Adjust the value of __cpp_constexpr.

	* class.c (trivial_default_constructor_is_constexpr): Return true in
	C++20.
	* constexpr.c (cx_check_missing_mem_inits): Allow missing field
	initializers in C++20.
	(cxx_eval_call_expression): Don't clear CONSTRUCTOR_NO_CLEARING for
	constexpr constructors in C++20.
	(reduced_constant_expression_p): Don't set FIELD for union and array
	types.  Skip empty class fields without initializers.
	* decl.c (check_for_uninitialized_const_var): Permit trivial default
	initialization in constexpr.
	(next_initializable_field): Don't skip vptr fields.
	* method.c (walk_field_subobs): Still consider a constructor that
	doesn't initialize all the members constexpr.

	* g++.dg/cpp0x/constexpr-array6.C: Adjust dg-error.
	* g++.dg/cpp0x/constexpr-ctor.C: Likewise.
	* g++.dg/cpp0x/constexpr-diag3.C: Likewise.
	* g++.dg/cpp0x/constexpr-diag4.C: Likewise.
	* g++.dg/cpp0x/constexpr-ex3.C: Likewise.
	* g++.dg/cpp0x/constexpr-template2.C: Likewise.
	* g++.dg/cpp0x/constexpr-union2.C: Likewise.
	* g++.dg/cpp0x/lambda/lambda-mangle.C: Rip out a piece of code ...
	* g++.dg/cpp0x/lambda/lambda-mangle6.C: ... and put it here.
	* g++.dg/cpp0x/pr79118.C: Adjust dg-error.
	* g++.dg/cpp1y/constexpr-83921-3.C: Likewise.
	* g++.dg/cpp1y/constexpr-neg1.C: Likewise.
	* g++.dg/cpp1z/constexpr-lambda12.C: Likewise.
	* g++.dg/cpp1z/feat-cxx1z.C: Use -std=c++17.
	* g++.dg/cpp2a/constexpr-init1.C: New test.
	* g++.dg/cpp2a/constexpr-init2.C: New test.
	* g++.dg/cpp2a/constexpr-init3.C: New test.
	* g++.dg/cpp2a/constexpr-init4.C: New test.
	* g++.dg/cpp2a/constexpr-init5.C: New test.
	* g++.dg/cpp2a/constexpr-init6.C: New test.
	* g++.dg/cpp2a/constexpr-init7.C: New test.
	* g++.dg/cpp2a/constexpr-init8.C: New test.
	* g++.dg/cpp2a/constexpr-init9.C: New test.
	* g++.dg/cpp2a/constexpr-init10.C: New test.
	* g++.dg/cpp2a/constexpr-init11.C: New test.
	* g++.dg/cpp2a/constexpr-init12.C: New test.
	* g++.dg/cpp2a/constexpr-init13.C: New test.
	* g++.dg/cpp2a/constexpr-init14.C: New test.
	* g++.dg/cpp2a/constexpr-init15.C: New test.
	* g++.dg/cpp2a/constexpr-try5.C: Adjust dg-error.
	* g++.dg/cpp2a/feat-cxx2a.C: Test __cpp_constexpr.
	* g++.dg/cpp2a/lambda-mangle.C: New test.
	* g++.dg/debug/dwarf2/pr44641.C: Skip for c++2a.
	* g++.dg/ext/stmtexpr21.C: Adjust dg-error.

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index c7f4659456a..5ad626d7f24 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -975,7 +975,8 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_fold_expressions=201603L");
 	  cpp_define (pfile, "__cpp_nontype_template_args=201411L");
 	  cpp_define (pfile, "__cpp_range_based_for=201603L");
-	  cpp_define (pfile, "__cpp_constexpr=201603L");
+	  if (cxx_dialect <= cxx17)
+	    cpp_define (pfile, "__cpp_constexpr=201603L");
 	  cpp_define (pfile, "__cpp_if_constexpr=201606L");
 	  cpp_define (pfile, "__cpp_capture_star_this=201603L");
 	  cpp_define (pfile, "__cpp_inline_variables=201606L");
@@ -997,6 +998,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_init_captures=201803L");
 	  cpp_define (pfile, "__cpp_generic_lambdas=201707L");
 	  cpp_define (pfile, "__cpp_designated_initializers=201707L");
+	  cpp_define (pfile, "__cpp_constexpr=201907L");
 	  cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
 	  cpp_define (pfile, "__cpp_conditional_explicit=201806L");
 	  cpp_define (pfile, "__cpp_consteval=201811L");
diff --git gcc/cp/class.c gcc/cp/class.c
index f36f75fa0db..d8bb44990b7 100644
--- gcc/cp/class.c
+++ gcc/cp/class.c
@@ -5288,8 +5288,14 @@ trivial_default_constructor_is_constexpr (tree t)
   /* A defaulted trivial default constructor is constexpr
      if there is nothing to initialize.  */
   gcc_assert (!TYPE_HAS_COMPLEX_DFLT (t));
-  /* A class with a vptr doesn't have a trivial default ctor.  */
-  return is_really_empty_class (t, /*ignore_vptr*/true);
+  /* A class with a vptr doesn't have a trivial default ctor.
+     In C++20, a class can have transient uninitialized members, e.g.:
+
+       struct S { int i; constexpr S() = default; };
+
+     should work.  */
+  return (cxx_dialect >= cxx2a
+	  || is_really_empty_class (t, /*ignore_vptr*/true));
 }
 
 /* Returns true iff class T has a constexpr default constructor.  */
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 370633467ab..0a27daf1f61 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -767,6 +767,10 @@ massage_constexpr_body (tree fun, tree body)
 static bool
 cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
 {
+  /* We allow uninitialized bases/fields in C++20.  */
+  if (cxx_dialect >= cxx2a)
+    return false;
+
   unsigned nelts = 0;
   
   if (body)
@@ -815,7 +819,7 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
 	    continue;
 	  if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
 	    {
-	      /* Recurse to check the anonummous aggregate member.  */
+	      /* Recurse to check the anonymous aggregate member.  */
 	      bad |= cx_check_missing_mem_inits
 		(TREE_TYPE (field), NULL_TREE, complain);
 	      if (bad && !complain)
@@ -2179,15 +2183,26 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	entry->result = result;
     }
 
-  /* The result of a constexpr function must be completely initialized.  */
-  if (TREE_CODE (result) == CONSTRUCTOR)
+  /* The result of a constexpr function must be completely initialized.
+
+     However, in C++20, a constexpr constructor doesn't necessarily have
+     to initialize all the fields, so we don't clear CONSTRUCTOR_NO_CLEARING
+     in order to detect reading an unitialized object in constexpr instead
+     of value-initializing it.  (reduced_constant_expression_p is expected to
+     take care of clearing the flag.)  */
+  if (TREE_CODE (result) == CONSTRUCTOR
+      && (cxx_dialect < cxx2a
+	  || !DECL_CONSTRUCTOR_P (fun)))
     clear_no_implicit_zero (result);
 
   pop_cx_call_context ();
   return result;
 }
 
-/* FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
+/* Return true if T is a valid constant initializer.  If a CONSTRUCTOR
+   initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
+   cleared.
+   FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
 
 bool
 reduced_constant_expression_p (tree t)
@@ -2209,6 +2224,12 @@ reduced_constant_expression_p (tree t)
 	  if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
 	    /* An initialized vector would have a VECTOR_CST.  */
 	    return false;
+	  else if (cxx_dialect >= cxx2a
+		   /* An ARRAY_TYPE doesn't have any TYPE_FIELDS.  */
+		   && (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
+		       /* A union only initializes one member.  */
+		       || TREE_CODE (TREE_TYPE (t)) == UNION_TYPE))
+	    field = NULL_TREE;
 	  else
 	    field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)));
 	}
@@ -2222,12 +2243,24 @@ reduced_constant_expression_p (tree t)
 	    return false;
 	  if (field)
 	    {
+	  retry:
 	      if (idx != field)
-		return false;
+		{
+		  /* Empty class field may or may not have an initializer.  */
+		  if (is_really_empty_class (TREE_TYPE (field),
+					     /*ignore_vptr*/false))
+		    {
+		      field = next_initializable_field (DECL_CHAIN (field));
+		      goto retry;
+		    }
+		  else
+		    return false;
+		}
 	      field = next_initializable_field (DECL_CHAIN (field));
 	    }
 	}
-      if (field)
+      if (field && !is_really_empty_class (TREE_TYPE (field),
+					   /*ignore_vptr*/false))
 	return false;
       else if (CONSTRUCTOR_NO_CLEARING (t))
 	/* All the fields are initialized.  */
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 481c798a2cf..bc8df3005ea 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5858,8 +5858,12 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
      7.1.6 */
   if (VAR_P (decl)
       && !TYPE_REF_P (type)
-      && (constexpr_context_p
-	  || CP_TYPE_CONST_P (type) || var_in_constexpr_fn (decl))
+      && (CP_TYPE_CONST_P (type)
+	  /* C++20 permits trivial default initialization in constexpr
+	     context (P1331R2).  */
+	  || (cxx_dialect < cxx2a
+	      && (constexpr_context_p
+		  || var_in_constexpr_fn (decl))))
       && !DECL_NONTRIVIALLY_INITIALIZED_P (decl))
     {
       tree field = default_init_uninitialized_part (type);
@@ -5868,7 +5872,7 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
 
       bool show_notes = true;
 
-      if (!constexpr_context_p)
+      if (!constexpr_context_p || cxx_dialect >= cxx2a)
 	{
 	  if (CP_TYPE_CONST_P (type))
 	    {
@@ -5938,7 +5942,11 @@ next_initializable_field (tree field)
 	 && (TREE_CODE (field) != FIELD_DECL
 	     || DECL_UNNAMED_BIT_FIELD (field)
 	     || (DECL_ARTIFICIAL (field)
-		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field)))))
+		 /* In C++17, don't skip base class fields.  */
+		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field))
+		 /* Don't skip vptr fields.  We might see them when we're
+		    called from reduced_constant_expression_p.  */
+		 && !DECL_VIRTUAL_P (field))))
     field = DECL_CHAIN (field);
 
   return field;
diff --git gcc/cp/method.c gcc/cp/method.c
index a707940cacc..d2aed473d77 100644
--- gcc/cp/method.c
+++ gcc/cp/method.c
@@ -1985,10 +1985,12 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname,
 	  if (bad && deleted_p)
 	    *deleted_p = true;
 
-	  /* For an implicitly-defined default constructor to be constexpr,
-	     every member must have a user-provided default constructor or
-	     an explicit initializer.  */
-	  if (constexpr_p && !CLASS_TYPE_P (mem_type)
+	  /* Before C++20, for an implicitly-defined default constructor to
+	     be constexpr, every member must have a user-provided default
+	     constructor or an explicit initializer.  */
+	  if (constexpr_p
+	      && cxx_dialect < cxx2a
+	      && !CLASS_TYPE_P (mem_type)
 	      && TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE)
 	    {
 	      *constexpr_p = false;
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
index 16eacdde440..48dae5d9609 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
@@ -4,7 +4,7 @@
 struct A
 {
   int i;
-  constexpr A() {}		// { dg-error "A::i" }
+  constexpr A() {}		// { dg-error "A::i" "" { target c++17_down } }
 };
 
 struct B
@@ -12,4 +12,5 @@ struct B
   A a;
 };
 
-constexpr B b[] = { {} };	// { dg-error "A::A" }
+constexpr B b[] = { {} };	// { dg-error "A::A" "" { target c++17_down } }
+// { dg-error "is not a constant expression" "" { target c++2a } .-1 }
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
index 55beda7c49f..1d0fa479cbc 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
@@ -3,5 +3,5 @@
 struct A
 {
   int i;
-  constexpr A() { }		// { dg-error "A::i" }
+  constexpr A() { }		// { dg-error "A::i" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
index e54b26c7f6a..1c43569615c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
@@ -48,7 +48,7 @@ struct Def
 {
   int _M_i;			// { dg-message "does not initialize" }
 
-  constexpr Def() = default;	// { dg-error "implicit declaration is not .constexpr." }
+  constexpr Def() = default;	// { dg-error "implicit declaration is not .constexpr." "" { target c++17_down } }
 };
 
 constexpr Def defobj;		// { dg-error "uninitialized" }
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
index 13ca6fa2390..c603bdd1a00 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
@@ -21,5 +21,5 @@ struct A1
 struct B1
 {
     A1 a1;
-    constexpr B1() {} // { dg-error "B1::a1" }
+    constexpr B1() {} // { dg-error "B1::a1" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
index a5893563eec..9d6d5ff587c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
@@ -6,7 +6,7 @@
 struct A
 {
   int i;
-  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" }
+  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" "" { target c++17_down } }
 };
 
 template <class T>
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
index 12a8d42b31f..71eb559a24c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
@@ -3,7 +3,7 @@
 template <class T> struct A
 {
   T t;
-  constexpr A() { }		// { dg-error "::t" }
+  constexpr A() { }		// { dg-error "::t" "" { target c++17_down } }
 };
 
 int main()
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
index 1a5e832ac34..c22ecc99efb 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
@@ -14,5 +14,5 @@ union bar
   int x;
   short y;
 
-  constexpr bar() = default;	// { dg-error "constexpr" }
+  constexpr bar() = default;	// { dg-error "constexpr" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
index 15b8b79ecbc..7894ef3051e 100644
--- gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
+++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
@@ -47,17 +47,6 @@ struct S {
 	 []{return 3;}());
 };
 
-template<typename T> struct R {
-  static int x;
-};
-// "int i;" makes the op() non-constexpr in C++17.
-template<typename T> int R<T>::x = []{int i; return 1;}();
-template int R<int>::x;
-// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
-// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
-// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
-// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
-
 void bar()
 {
   // lambdas in non-vague linkage functions have internal linkage.
diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C
new file mode 100644
index 00000000000..9ec13e79bbf
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C
@@ -0,0 +1,15 @@
+// Test lambda mangling
+// { dg-do compile { target { c++11 && c++17_down } } }
+// { dg-require-weak "" }
+// { dg-options "-fno-inline" }
+
+template<typename T> struct R {
+  static int x;
+};
+// "int i;" makes the op() non-constexpr in C++17.  In C++20, it does not.
+template<typename T> int R<T>::x = []{int i; return 1;}();
+template int R<int>::x;
+// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
+// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
+// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
+// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
diff --git gcc/testsuite/g++.dg/cpp0x/pr79118.C gcc/testsuite/g++.dg/cpp0x/pr79118.C
index 5db22a96dd4..616b51ea29a 100644
--- gcc/testsuite/g++.dg/cpp0x/pr79118.C
+++ gcc/testsuite/g++.dg/cpp0x/pr79118.C
@@ -13,7 +13,7 @@ struct One
   constexpr One () : a(), b() {} // { dg-error "multiple" }
   constexpr One (int) : a() {}
   constexpr One (unsigned) : b () {}
-  constexpr One (void *) {} // { dg-error "exactly one" }
+  constexpr One (void *) {} // { dg-error "exactly one" "" { target c++17_down } }
 };
 
 One a ();
@@ -30,10 +30,10 @@ struct Two
   };
 
   constexpr Two () : a(), b() {}
-  constexpr Two (int) : a() {} // { dg-error "b' must be initialized" }
-  constexpr Two (unsigned) : b () {} // { dg-error "a' must be initialized" }
-  constexpr Two (void *) {} // { dg-error "a' must be initialized" }
-   // { dg-error "b' must be initialized" "" { target *-*-* } .-1 }
+  constexpr Two (int) : a() {} // { dg-error "b' must be initialized" "" { target c++17_down } }
+  constexpr Two (unsigned) : b () {} // { dg-error "a' must be initialized" "" { target c++17_down } }
+  constexpr Two (void *) {} // { dg-error "a' must be initialized" "" { target c++17_down } }
+   // { dg-error "b' must be initialized" "" { target c++17_down } .-1 }
 };
 
 Two e ();
diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
index 4b1ed5c3a87..2f1218693e0 100644
--- gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
@@ -2,4 +2,4 @@
 // { dg-do compile { target c++14 } }
 
 struct Foo { int m; };
-constexpr void test() { Foo f; }  // { dg-error "uninitialized" }
+constexpr void test() { Foo f; }  // { dg-error "uninitialized" "" { target c++17_down } }
diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
index d82dbada1bf..53f0f1f7a2b 100644
--- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
@@ -8,7 +8,7 @@ constexpr int f(int i) {
   goto foo;			// { dg-error "goto" }
  foo:
   asm("foo");			// { dg-error "asm" "" { target c++17_down } }
-  int k;			// { dg-error "uninitialized" }
+  int k;			// { dg-error "uninitialized" "" { target c++17_down } }
   A a;				// { dg-error "non-literal" }
   return i;
 }
diff --git gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
index a59bf497b30..93b53273741 100644
--- gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
@@ -3,7 +3,7 @@
 void f(int i)
 {
   [i]() constexpr {
-    int j;			// { dg-error "uninitialized" }
+    int j;			// { dg-error "uninitialized" "" { target c++17_down } }
     j = i;
     return j;
   }();
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
new file mode 100644
index 00000000000..ab7b89da9e8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
@@ -0,0 +1,99 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+// Test basic use.
+
+struct S {
+  int i;
+  constexpr S(bool b) {
+    if (b)
+      i = 42;
+  }
+};
+constexpr S s1(true);
+constexpr S s2(false); // { dg-error "not a constant expression" }
+
+constexpr int
+fn1 (int x)
+{
+  int a;
+  a = 5;
+  return x + a;
+}
+
+static_assert (fn1 (2) == 7);
+
+constexpr int
+fn2 (int x)
+{
+  const int a; // { dg-error "uninitialized .const a." }
+  constexpr int b; // { dg-error "uninitialized .const b." }
+  return x;
+}
+
+constexpr int
+fn3 (int x)
+{
+  int a; // { dg-message ".int a. is not const" }
+  return x + a; // { dg-error "the value of .a. is not usable in a constant expression" }
+}
+
+constexpr int a = fn3 (5); // { dg-message "in .constexpr. expansion of" }
+
+constexpr int
+fn4 ()
+{
+  struct S { int a = -5; int b; } s;
+  return s.a;
+}
+
+static_assert (fn4 () == -5);
+
+constexpr int
+fn5 ()
+{
+  struct S { int a = 9; int b; } s;
+  return s.b;
+}
+
+constexpr int b = fn5 (); // { dg-error "accessing uninitialized member" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+
+constexpr int
+fn6 ()
+{
+  int a;
+  return 42;
+}
+
+static_assert (fn6 () == 42);
+
+constexpr int
+fn7 (bool b)
+{
+  int a; // { dg-message ".int a. is not const" }
+  if (b)
+    a = 42;
+  return a;
+}
+
+static_assert (fn7 (true) == 42);
+static_assert (fn7 (false) == 42); // { dg-error "non-constant condition|the value of .a. is not usable" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+
+constexpr int
+fn8 (int n)
+{
+  int r;
+  switch (n)
+    {
+    case 1:
+    r = n;
+    return r;
+    case 42:
+    r = n;
+    return r;
+    }
+}
+
+static_assert (fn8 (1) == 1);
+static_assert (fn8 (42) == 42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C
new file mode 100644
index 00000000000..74bf8e6677b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C
@@ -0,0 +1,11 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+// In c++2a we don't emit a call to _ZN3FooI3ArgEC1Ev.
+
+struct Arg;
+struct Base {
+  int i;
+  virtual ~Base();
+};
+template <class> struct Foo : Base { };
+Foo<Arg> a;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C
new file mode 100644
index 00000000000..1c7836a674a
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C
@@ -0,0 +1,16 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int i;
+  constexpr S(int) : i(10) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C
new file mode 100644
index 00000000000..7d3d3729b31
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C
@@ -0,0 +1,16 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int uninit;
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42); // { dg-error "not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C
new file mode 100644
index 00000000000..3d4460a0eb8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C
@@ -0,0 +1,37 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct E { };
+
+struct S {
+  E e;
+  constexpr S() {}
+};
+
+constexpr S s;
+constexpr S s2[4];
+
+struct W {
+  [[no_unique_address]] E e1, e2;
+  constexpr W() {}
+};
+
+constexpr W w;
+constexpr W w2[4];
+
+struct Y {
+  [[no_unique_address]] E e;
+  __extension__ char a[0];
+  constexpr Y() {}
+};
+
+constexpr Y y;
+constexpr Y y2[4];
+
+struct Z {
+  [[no_unique_address]] E e;
+  int i;
+  constexpr Z(int n) :i(n) { }
+};
+
+constexpr Z z(42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C
new file mode 100644
index 00000000000..6ab6abf1505
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C
@@ -0,0 +1,28 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct E {
+  constexpr E() = default;
+  constexpr E(int) {}
+};
+
+struct W {
+  [[no_unique_address]] E e;
+  constexpr W(int) : e(8) {}
+};
+
+constexpr W w = W(42);
+
+struct S {
+  E e;
+  constexpr S() : e{} { }
+};
+
+constexpr S s;
+
+struct S2 {
+  [[no_unique_address]] E e;
+  constexpr S2() : e{} { }
+};
+
+constexpr S2 s2;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init15.C gcc/testsuite/g++.dg/cpp2a/constexpr-init15.C
new file mode 100644
index 00000000000..f80d3f2c27c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init15.C
@@ -0,0 +1,31 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct E {
+  constexpr E() = default;
+  constexpr E(int) {}
+};
+
+struct W {
+  [[no_unique_address]] E e;
+  int i;
+  constexpr W(int) : e(8), i(11) {}
+};
+
+constexpr W w = W(42);
+
+struct S {
+  E e;
+  int i;
+  constexpr S() : e{}, i(11) { }
+};
+
+constexpr S s;
+
+struct S2 {
+  [[no_unique_address]] E e;
+  int i;
+  constexpr S2() : e{}, i(11) { }
+};
+
+constexpr S2 s2;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C
new file mode 100644
index 00000000000..541da1c023f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C
@@ -0,0 +1,15 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+  int i;
+  constexpr A() : i{} {} 
+};
+
+struct B
+{
+  A a;
+};
+
+constexpr B b[] = { {} };
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C
new file mode 100644
index 00000000000..dd2735289cb
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C
@@ -0,0 +1,16 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+  int i;
+  constexpr A() {} 
+};
+
+struct B
+{
+  A a;
+};
+
+// A::i not initialized.
+constexpr B b[] = { {} }; // { dg-error "is not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C
new file mode 100644
index 00000000000..dd614ede2c6
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C
@@ -0,0 +1,61 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+// This bullet in [dcl.constexpr] is now gone:
+//  - every non-static data member and base class sub-object shall be initialized
+
+struct A {
+  int i;
+  constexpr A(int _i) { i = _i; }
+};
+
+struct B {
+  int i;
+  constexpr B() { }
+};
+
+// Anonymous members.
+struct E {
+  int a;
+  union {
+    char b;
+    __extension__ struct {
+      double c;
+      long d;
+    };  
+    union {
+      char e;
+      void *f; 
+    };  
+  };  
+  __extension__ struct {
+    long long g;
+    __extension__ struct {
+      int h;
+      double i;
+    };  
+    union {
+      char *j; 
+      E *k; 
+    };  
+  };  
+
+  // Completely initialized.
+  constexpr E(int(&)[1]) : a(), b(), g(), h(), i(), j() {}
+  constexpr E(int(&)[3]) : a(), e(), g(), h(), i(), k() {}
+  constexpr E(int(&)[7]) : a(), b(), g(), h(), i(), j() {}
+  constexpr E(int(&)[8]) : a(), f(), g(), h(), i(), k() {}
+  constexpr E(int(&)[9]) : a(), c(), d(), g(), h(), i(), k() {}
+
+  // Missing d, i, j/k union init.
+  constexpr E(int(&)[2]) : a(), c(), g(), h() {}
+
+  // Missing h, j/k union init.
+  constexpr E(int(&)[4]) : a(), c(), d(), g(), i() {}
+
+  // Missing b/c/d/e/f union init.
+  constexpr E(int(&)[5]) : a(), g(), h(), i(), k() {}
+
+  // Missing a, b/c/d/e/f union, g/h/i/j/k struct init.
+  constexpr E(int(&)[6]) {}
+};
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C
new file mode 100644
index 00000000000..0d21f26da0e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C
@@ -0,0 +1,22 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S { int i; };
+
+constexpr void
+fn ()
+{
+  S s;
+
+  []() constexpr {
+    int i;
+  }();
+}
+
+constexpr int
+fn2 ()
+{
+  return __extension__ ({ int n; n; }); // { dg-error "not usable in a constant expression" }
+}
+
+constexpr int i = fn2 (); // { dg-message "in .constexpr. expansion of" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C
new file mode 100644
index 00000000000..a2994f5272c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C
@@ -0,0 +1,26 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+/* We used to get the "constexpr constructor for union S::<unnamed union>
+   must initialize exactly one non-static data member" error, but not anymore
+   in C++20.  */
+
+struct S {
+  union {
+    int i;
+    double d;
+  };
+  constexpr S() { }
+};
+
+union U {
+  int a;
+  constexpr U() { }
+};
+
+struct W {
+  union {
+    int a;
+  };
+  constexpr W() { }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C
new file mode 100644
index 00000000000..dd2741efa8c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C
@@ -0,0 +1,63 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int a = 1;
+  constexpr S() = default;
+};
+
+constexpr S s;
+
+union U {
+  int a = 1;
+  constexpr U() = default;
+};
+
+constexpr U u;
+
+struct S2 {
+  int a;
+  constexpr S2() = default;
+};
+
+constexpr S2 s2; // { dg-error "uninitialized .const s2." }
+
+union U2 {
+  int a;
+  constexpr U2() = default;
+};
+
+constexpr U2 u2; // { dg-error "uninitialized .const u2." }
+
+struct S3 {
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int a;
+  } u;
+  constexpr S3() = default;
+};
+
+constexpr S3 s3; // { dg-error "uninitialized .const s3." }
+
+struct S4 {
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int n;
+  } u;
+  constexpr S4() = default;
+};
+
+constexpr S4 s4; // { dg-error "uninitialized .const s4." }
+
+struct S5 {
+  union {
+    int n = 0;
+  };
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int m;
+  } u;
+  constexpr S5() = default;
+};
+
+constexpr S5 s5; // { dg-error "uninitialized .const s5.|not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C
new file mode 100644
index 00000000000..0d5a4a79c90
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C
@@ -0,0 +1,15 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C
new file mode 100644
index 00000000000..b44098cc89b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C
@@ -0,0 +1,17 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int (&)[8]) : W(8) { }
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+int arr[8];
+constexpr auto a = W(arr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
index 47cdce88e36..3b51bf7c901 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
@@ -4,13 +4,13 @@
 
 constexpr int foo ()
 try {			// { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } }
-  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" }
+  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
   static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
   goto l;		// { dg-error "'goto' in 'constexpr' function" }
   l:;
   return 0;
 } catch (...) {
-  long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" }
+  long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
   static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function" }
   goto l2;		// { dg-error "'goto' in 'constexpr' function" }
   l2:;
@@ -19,19 +19,19 @@ try {			// { dg-warning "function-try-block body of 'constexpr' function only av
 
 constexpr int bar ()
 {
-  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" }
+  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
   static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
   goto l;		// { dg-error "'goto' in 'constexpr' function" }
   l:;
   try {			// { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } }
-    short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" }
+    short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
     static float d;	// { dg-error "'d' declared 'static' in 'constexpr' function" }
-			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target *-*-* } .-1 }
+			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target c++17_down } .-1 }
     goto l2;		// { dg-error "'goto' in 'constexpr' function" }
     l2:;
     return 0;
   } catch (int) {
-    char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" }
+    char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" "" { target c++17_down } }
     static int f = 5;	// { dg-error "'f' declared 'static' in 'constexpr' function" }
     goto l3;		// { dg-error "'goto' in 'constexpr' function" }
     l3:;
diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 389b25e16ea..c86aead79af 100644
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -134,8 +134,8 @@
 
 #ifndef __cpp_constexpr
 #  error "__cpp_constexpr"
-#elif __cpp_constexpr != 201603
-#  error "__cpp_constexpr != 201603"
+#elif __cpp_constexpr != 201907
+#  error "__cpp_constexpr != 201907"
 #endif
 
 #ifndef __cpp_decltype_auto
diff --git gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C
new file mode 100644
index 00000000000..8ee9b0327a5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C
@@ -0,0 +1,15 @@
+// Test lambda mangling
+// { dg-do compile { target c++2a } }
+// { dg-require-weak "" }
+// { dg-options "-fno-inline" }
+
+template<typename T> struct R {
+  static int x;
+};
+// "int i;" makes the op() non-constexpr in C++17.  In C++20, it does not.
+template<typename T> int R<T>::x = []{int i; return 1;}();
+template int R<int>::x;
+// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
+// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
+// { dg-final { scan-assembler-not "_ZNK1RIiE1xMUlvE_clEv" } }
+// { dg-final { scan-assembler-not "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" } }
diff --git gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
index d50df25f693..1139f412fda 100644
--- gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
+++ gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
@@ -1,5 +1,5 @@
 // Origin: PR 44641
-// { dg-do compile }
+// { dg-do compile { target c++17_down } }
 // { dg-options "-gdwarf-2 -O0 -dA" }
 
 template <class A> struct MisplacedDbg;
@@ -40,3 +40,11 @@ struct MisplacedDbg  // { dg-function-on-line {_ZN12MisplacedDbgI3ArgEC[12]Ev} {
 static MisplacedDbg<Arg> static_var1;
 static MisplacedDbg<Arg*> static_var2;
 static MisplacedDbg<Full> static_var3;
+
+// This test is skipped in C++20 because we consider the default constructor
+// MisplacedDbg() constexpr despite the uninitialized member "int i;".  So
+// the calls to
+//    MisplacedDbg<Arg>::MisplacedDbg()
+//    MisplacedDbg<Full>::MisplacedDbg()
+//    MisplacedDbg<Arg*>::MisplacedDbg()
+// are elided.  (This comment is here not to mess up the line numbers.)
diff --git gcc/testsuite/g++.dg/ext/stmtexpr21.C gcc/testsuite/g++.dg/ext/stmtexpr21.C
index 259cb2f1913..97052a117a7 100644
--- gcc/testsuite/g++.dg/ext/stmtexpr21.C
+++ gcc/testsuite/g++.dg/ext/stmtexpr21.C
@@ -7,7 +7,7 @@ struct test { const int *addr; };
 const test* setup()
 {
   static constexpr test atest =
-    { ({ int inner; (const int*)(0); }) };  // { dg-error "uninitialized" }
+    { ({ int inner; (const int*)(0); }) };  // { dg-error "uninitialized" "" { target c++17_down } }
 
   return &atest;
 }
Jason Merrill Dec. 5, 2019, 6:37 p.m. UTC | #18
On 12/4/19 5:43 PM, Marek Polacek wrote:
> @@ -2222,12 +2243,24 @@ reduced_constant_expression_p (tree t)
>   	    return false;
>   	  if (field)
>   	    {
> +	  retry:
>   	      if (idx != field)
> -		return false;
> +		{
> +		  /* Empty class field may or may not have an initializer.  */
> +		  if (is_really_empty_class (TREE_TYPE (field),
> +					     /*ignore_vptr*/false))
> +		    {
> +		      field = next_initializable_field (DECL_CHAIN (field));
> +		      goto retry;
> +		    }
> +		  else
> +		    return false;
> +		}

I'm not as anti-goto as many people, but it would be shorter to write 
this as a loop:

for (; idx != field;
      field = next_initializable_field (DECL_CHAIN (field))
   if (!is_really_empty_class (TREE_TYPE (field), false))
     return false;

>   	      field = next_initializable_field (DECL_CHAIN (field));
>   	    }
>   	}
> -      if (field)
> +      if (field && !is_really_empty_class (TREE_TYPE (field),
> +					   /*ignore_vptr*/false))
>   	return false;

And I think we should loop here, too, in case there's a non-empty field 
at the end.

Jason
Marek Polacek Dec. 5, 2019, 7:28 p.m. UTC | #19
On Thu, Dec 05, 2019 at 01:37:45PM -0500, Jason Merrill wrote:
> On 12/4/19 5:43 PM, Marek Polacek wrote:
> > @@ -2222,12 +2243,24 @@ reduced_constant_expression_p (tree t)
> >   	    return false;
> >   	  if (field)
> >   	    {
> > +	  retry:
> >   	      if (idx != field)
> > -		return false;
> > +		{
> > +		  /* Empty class field may or may not have an initializer.  */
> > +		  if (is_really_empty_class (TREE_TYPE (field),
> > +					     /*ignore_vptr*/false))
> > +		    {
> > +		      field = next_initializable_field (DECL_CHAIN (field));
> > +		      goto retry;
> > +		    }
> > +		  else
> > +		    return false;
> > +		}
> 
> I'm not as anti-goto as many people, but it would be shorter to write this
> as a loop:
> 
> for (; idx != field;
>      field = next_initializable_field (DECL_CHAIN (field))
>   if (!is_really_empty_class (TREE_TYPE (field), false))
>     return false;

Works for me!  Adjusted ...

> >   	      field = next_initializable_field (DECL_CHAIN (field));
> >   	    }
> >   	}
> > -      if (field)
> > +      if (field && !is_really_empty_class (TREE_TYPE (field),
> > +					   /*ignore_vptr*/false))
> >   	return false;
> 
> And I think we should loop here, too, in case there's a non-empty field at
> the end.

... and here too, thanks.  No other changes otherwise.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2019-12-05  Marek Polacek  <polacek@redhat.com>
	    Jakub Jelinek  <jakub@redhat.com>

	PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
	* c-cppbuiltin.c (c_cpp_builtins): Adjust the value of __cpp_constexpr.

	* class.c (trivial_default_constructor_is_constexpr): Return true in
	C++20.
	* constexpr.c (cx_check_missing_mem_inits): Allow missing field
	initializers in C++20.
	(cxx_eval_call_expression): Don't clear CONSTRUCTOR_NO_CLEARING for
	constexpr constructors in C++20.
	(reduced_constant_expression_p): Don't set FIELD for union and array
	types.  Skip empty class fields without initializers.
	* decl.c (check_for_uninitialized_const_var): Permit trivial default
	initialization in constexpr.
	(next_initializable_field): Don't skip vptr fields.
	* method.c (walk_field_subobs): Still consider a constructor that
	doesn't initialize all the members constexpr.

	* g++.dg/cpp0x/constexpr-array6.C: Adjust dg-error.
	* g++.dg/cpp0x/constexpr-ctor.C: Likewise.
	* g++.dg/cpp0x/constexpr-diag3.C: Likewise.
	* g++.dg/cpp0x/constexpr-diag4.C: Likewise.
	* g++.dg/cpp0x/constexpr-ex3.C: Likewise.
	* g++.dg/cpp0x/constexpr-template2.C: Likewise.
	* g++.dg/cpp0x/constexpr-union2.C: Likewise.
	* g++.dg/cpp0x/lambda/lambda-mangle.C: Rip out a piece of code ...
	* g++.dg/cpp0x/lambda/lambda-mangle6.C: ... and put it here.
	* g++.dg/cpp0x/pr79118.C: Adjust dg-error.
	* g++.dg/cpp1y/constexpr-83921-3.C: Likewise.
	* g++.dg/cpp1y/constexpr-neg1.C: Likewise.
	* g++.dg/cpp1z/constexpr-lambda12.C: Likewise.
	* g++.dg/cpp1z/feat-cxx1z.C: Use -std=c++17.
	* g++.dg/cpp2a/constexpr-init1.C: New test.
	* g++.dg/cpp2a/constexpr-init2.C: New test.
	* g++.dg/cpp2a/constexpr-init3.C: New test.
	* g++.dg/cpp2a/constexpr-init4.C: New test.
	* g++.dg/cpp2a/constexpr-init5.C: New test.
	* g++.dg/cpp2a/constexpr-init6.C: New test.
	* g++.dg/cpp2a/constexpr-init7.C: New test.
	* g++.dg/cpp2a/constexpr-init8.C: New test.
	* g++.dg/cpp2a/constexpr-init9.C: New test.
	* g++.dg/cpp2a/constexpr-init10.C: New test.
	* g++.dg/cpp2a/constexpr-init11.C: New test.
	* g++.dg/cpp2a/constexpr-init12.C: New test.
	* g++.dg/cpp2a/constexpr-init13.C: New test.
	* g++.dg/cpp2a/constexpr-init14.C: New test.
	* g++.dg/cpp2a/constexpr-init15.C: New test.
	* g++.dg/cpp2a/constexpr-try5.C: Adjust dg-error.
	* g++.dg/cpp2a/feat-cxx2a.C: Test __cpp_constexpr.
	* g++.dg/cpp2a/lambda-mangle.C: New test.
	* g++.dg/debug/dwarf2/pr44641.C: Skip for c++2a.
	* g++.dg/ext/stmtexpr21.C: Adjust dg-error.

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index c7f4659456a..5ad626d7f24 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -975,7 +975,8 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_fold_expressions=201603L");
 	  cpp_define (pfile, "__cpp_nontype_template_args=201411L");
 	  cpp_define (pfile, "__cpp_range_based_for=201603L");
-	  cpp_define (pfile, "__cpp_constexpr=201603L");
+	  if (cxx_dialect <= cxx17)
+	    cpp_define (pfile, "__cpp_constexpr=201603L");
 	  cpp_define (pfile, "__cpp_if_constexpr=201606L");
 	  cpp_define (pfile, "__cpp_capture_star_this=201603L");
 	  cpp_define (pfile, "__cpp_inline_variables=201606L");
@@ -997,6 +998,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_init_captures=201803L");
 	  cpp_define (pfile, "__cpp_generic_lambdas=201707L");
 	  cpp_define (pfile, "__cpp_designated_initializers=201707L");
+	  cpp_define (pfile, "__cpp_constexpr=201907L");
 	  cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
 	  cpp_define (pfile, "__cpp_conditional_explicit=201806L");
 	  cpp_define (pfile, "__cpp_consteval=201811L");
diff --git gcc/cp/class.c gcc/cp/class.c
index f36f75fa0db..d8bb44990b7 100644
--- gcc/cp/class.c
+++ gcc/cp/class.c
@@ -5288,8 +5288,14 @@ trivial_default_constructor_is_constexpr (tree t)
   /* A defaulted trivial default constructor is constexpr
      if there is nothing to initialize.  */
   gcc_assert (!TYPE_HAS_COMPLEX_DFLT (t));
-  /* A class with a vptr doesn't have a trivial default ctor.  */
-  return is_really_empty_class (t, /*ignore_vptr*/true);
+  /* A class with a vptr doesn't have a trivial default ctor.
+     In C++20, a class can have transient uninitialized members, e.g.:
+
+       struct S { int i; constexpr S() = default; };
+
+     should work.  */
+  return (cxx_dialect >= cxx2a
+	  || is_really_empty_class (t, /*ignore_vptr*/true));
 }
 
 /* Returns true iff class T has a constexpr default constructor.  */
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 370633467ab..19e09c74760 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -767,6 +767,10 @@ massage_constexpr_body (tree fun, tree body)
 static bool
 cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
 {
+  /* We allow uninitialized bases/fields in C++20.  */
+  if (cxx_dialect >= cxx2a)
+    return false;
+
   unsigned nelts = 0;
   
   if (body)
@@ -815,7 +819,7 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
 	    continue;
 	  if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
 	    {
-	      /* Recurse to check the anonummous aggregate member.  */
+	      /* Recurse to check the anonymous aggregate member.  */
 	      bad |= cx_check_missing_mem_inits
 		(TREE_TYPE (field), NULL_TREE, complain);
 	      if (bad && !complain)
@@ -2179,15 +2183,26 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	entry->result = result;
     }
 
-  /* The result of a constexpr function must be completely initialized.  */
-  if (TREE_CODE (result) == CONSTRUCTOR)
+  /* The result of a constexpr function must be completely initialized.
+
+     However, in C++20, a constexpr constructor doesn't necessarily have
+     to initialize all the fields, so we don't clear CONSTRUCTOR_NO_CLEARING
+     in order to detect reading an unitialized object in constexpr instead
+     of value-initializing it.  (reduced_constant_expression_p is expected to
+     take care of clearing the flag.)  */
+  if (TREE_CODE (result) == CONSTRUCTOR
+      && (cxx_dialect < cxx2a
+	  || !DECL_CONSTRUCTOR_P (fun)))
     clear_no_implicit_zero (result);
 
   pop_cx_call_context ();
   return result;
 }
 
-/* FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
+/* Return true if T is a valid constant initializer.  If a CONSTRUCTOR
+   initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
+   cleared.
+   FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
 
 bool
 reduced_constant_expression_p (tree t)
@@ -2209,6 +2224,12 @@ reduced_constant_expression_p (tree t)
 	  if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
 	    /* An initialized vector would have a VECTOR_CST.  */
 	    return false;
+	  else if (cxx_dialect >= cxx2a
+		   /* An ARRAY_TYPE doesn't have any TYPE_FIELDS.  */
+		   && (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
+		       /* A union only initializes one member.  */
+		       || TREE_CODE (TREE_TYPE (t)) == UNION_TYPE))
+	    field = NULL_TREE;
 	  else
 	    field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)));
 	}
@@ -2222,14 +2243,20 @@ reduced_constant_expression_p (tree t)
 	    return false;
 	  if (field)
 	    {
-	      if (idx != field)
-		return false;
+	      /* Empty class field may or may not have an initializer.  */
+	      for (; idx != field;
+		   field = next_initializable_field (DECL_CHAIN (field)))
+		if (!is_really_empty_class (TREE_TYPE (field),
+					    /*ignore_vptr*/false))
+		  return false;
 	      field = next_initializable_field (DECL_CHAIN (field));
 	    }
 	}
-      if (field)
-	return false;
-      else if (CONSTRUCTOR_NO_CLEARING (t))
+      /* There could be a non-empty field at the end.  */
+      for (; field; field = next_initializable_field (DECL_CHAIN (field)))
+	if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/false))
+	  return false;
+      if (CONSTRUCTOR_NO_CLEARING (t))
 	/* All the fields are initialized.  */
 	CONSTRUCTOR_NO_CLEARING (t) = false;
       return true;
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 7897327ad9a..a44f1721ea2 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5858,8 +5858,12 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
      7.1.6 */
   if (VAR_P (decl)
       && !TYPE_REF_P (type)
-      && (constexpr_context_p
-	  || CP_TYPE_CONST_P (type) || var_in_constexpr_fn (decl))
+      && (CP_TYPE_CONST_P (type)
+	  /* C++20 permits trivial default initialization in constexpr
+	     context (P1331R2).  */
+	  || (cxx_dialect < cxx2a
+	      && (constexpr_context_p
+		  || var_in_constexpr_fn (decl))))
       && !DECL_NONTRIVIALLY_INITIALIZED_P (decl))
     {
       tree field = default_init_uninitialized_part (type);
@@ -5868,7 +5872,7 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
 
       bool show_notes = true;
 
-      if (!constexpr_context_p)
+      if (!constexpr_context_p || cxx_dialect >= cxx2a)
 	{
 	  if (CP_TYPE_CONST_P (type))
 	    {
@@ -5938,7 +5942,11 @@ next_initializable_field (tree field)
 	 && (TREE_CODE (field) != FIELD_DECL
 	     || DECL_UNNAMED_BIT_FIELD (field)
 	     || (DECL_ARTIFICIAL (field)
-		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field)))))
+		 /* In C++17, don't skip base class fields.  */
+		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field))
+		 /* Don't skip vptr fields.  We might see them when we're
+		    called from reduced_constant_expression_p.  */
+		 && !DECL_VIRTUAL_P (field))))
     field = DECL_CHAIN (field);
 
   return field;
diff --git gcc/cp/method.c gcc/cp/method.c
index a707940cacc..d2aed473d77 100644
--- gcc/cp/method.c
+++ gcc/cp/method.c
@@ -1985,10 +1985,12 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname,
 	  if (bad && deleted_p)
 	    *deleted_p = true;
 
-	  /* For an implicitly-defined default constructor to be constexpr,
-	     every member must have a user-provided default constructor or
-	     an explicit initializer.  */
-	  if (constexpr_p && !CLASS_TYPE_P (mem_type)
+	  /* Before C++20, for an implicitly-defined default constructor to
+	     be constexpr, every member must have a user-provided default
+	     constructor or an explicit initializer.  */
+	  if (constexpr_p
+	      && cxx_dialect < cxx2a
+	      && !CLASS_TYPE_P (mem_type)
 	      && TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE)
 	    {
 	      *constexpr_p = false;
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
index 16eacdde440..48dae5d9609 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
@@ -4,7 +4,7 @@
 struct A
 {
   int i;
-  constexpr A() {}		// { dg-error "A::i" }
+  constexpr A() {}		// { dg-error "A::i" "" { target c++17_down } }
 };
 
 struct B
@@ -12,4 +12,5 @@ struct B
   A a;
 };
 
-constexpr B b[] = { {} };	// { dg-error "A::A" }
+constexpr B b[] = { {} };	// { dg-error "A::A" "" { target c++17_down } }
+// { dg-error "is not a constant expression" "" { target c++2a } .-1 }
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
index 55beda7c49f..1d0fa479cbc 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
@@ -3,5 +3,5 @@
 struct A
 {
   int i;
-  constexpr A() { }		// { dg-error "A::i" }
+  constexpr A() { }		// { dg-error "A::i" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
index e54b26c7f6a..1c43569615c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
@@ -48,7 +48,7 @@ struct Def
 {
   int _M_i;			// { dg-message "does not initialize" }
 
-  constexpr Def() = default;	// { dg-error "implicit declaration is not .constexpr." }
+  constexpr Def() = default;	// { dg-error "implicit declaration is not .constexpr." "" { target c++17_down } }
 };
 
 constexpr Def defobj;		// { dg-error "uninitialized" }
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
index 13ca6fa2390..c603bdd1a00 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
@@ -21,5 +21,5 @@ struct A1
 struct B1
 {
     A1 a1;
-    constexpr B1() {} // { dg-error "B1::a1" }
+    constexpr B1() {} // { dg-error "B1::a1" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
index a5893563eec..9d6d5ff587c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
@@ -6,7 +6,7 @@
 struct A
 {
   int i;
-  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" }
+  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" "" { target c++17_down } }
 };
 
 template <class T>
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
index 12a8d42b31f..71eb559a24c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
@@ -3,7 +3,7 @@
 template <class T> struct A
 {
   T t;
-  constexpr A() { }		// { dg-error "::t" }
+  constexpr A() { }		// { dg-error "::t" "" { target c++17_down } }
 };
 
 int main()
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
index 1a5e832ac34..c22ecc99efb 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
@@ -14,5 +14,5 @@ union bar
   int x;
   short y;
 
-  constexpr bar() = default;	// { dg-error "constexpr" }
+  constexpr bar() = default;	// { dg-error "constexpr" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
index 15b8b79ecbc..7894ef3051e 100644
--- gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
+++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
@@ -47,17 +47,6 @@ struct S {
 	 []{return 3;}());
 };
 
-template<typename T> struct R {
-  static int x;
-};
-// "int i;" makes the op() non-constexpr in C++17.
-template<typename T> int R<T>::x = []{int i; return 1;}();
-template int R<int>::x;
-// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
-// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
-// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
-// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
-
 void bar()
 {
   // lambdas in non-vague linkage functions have internal linkage.
diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C
new file mode 100644
index 00000000000..9ec13e79bbf
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C
@@ -0,0 +1,15 @@
+// Test lambda mangling
+// { dg-do compile { target { c++11 && c++17_down } } }
+// { dg-require-weak "" }
+// { dg-options "-fno-inline" }
+
+template<typename T> struct R {
+  static int x;
+};
+// "int i;" makes the op() non-constexpr in C++17.  In C++20, it does not.
+template<typename T> int R<T>::x = []{int i; return 1;}();
+template int R<int>::x;
+// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
+// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
+// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
+// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
diff --git gcc/testsuite/g++.dg/cpp0x/pr79118.C gcc/testsuite/g++.dg/cpp0x/pr79118.C
index 5db22a96dd4..616b51ea29a 100644
--- gcc/testsuite/g++.dg/cpp0x/pr79118.C
+++ gcc/testsuite/g++.dg/cpp0x/pr79118.C
@@ -13,7 +13,7 @@ struct One
   constexpr One () : a(), b() {} // { dg-error "multiple" }
   constexpr One (int) : a() {}
   constexpr One (unsigned) : b () {}
-  constexpr One (void *) {} // { dg-error "exactly one" }
+  constexpr One (void *) {} // { dg-error "exactly one" "" { target c++17_down } }
 };
 
 One a ();
@@ -30,10 +30,10 @@ struct Two
   };
 
   constexpr Two () : a(), b() {}
-  constexpr Two (int) : a() {} // { dg-error "b' must be initialized" }
-  constexpr Two (unsigned) : b () {} // { dg-error "a' must be initialized" }
-  constexpr Two (void *) {} // { dg-error "a' must be initialized" }
-   // { dg-error "b' must be initialized" "" { target *-*-* } .-1 }
+  constexpr Two (int) : a() {} // { dg-error "b' must be initialized" "" { target c++17_down } }
+  constexpr Two (unsigned) : b () {} // { dg-error "a' must be initialized" "" { target c++17_down } }
+  constexpr Two (void *) {} // { dg-error "a' must be initialized" "" { target c++17_down } }
+   // { dg-error "b' must be initialized" "" { target c++17_down } .-1 }
 };
 
 Two e ();
diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
index 4b1ed5c3a87..2f1218693e0 100644
--- gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
@@ -2,4 +2,4 @@
 // { dg-do compile { target c++14 } }
 
 struct Foo { int m; };
-constexpr void test() { Foo f; }  // { dg-error "uninitialized" }
+constexpr void test() { Foo f; }  // { dg-error "uninitialized" "" { target c++17_down } }
diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
index d82dbada1bf..53f0f1f7a2b 100644
--- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
@@ -8,7 +8,7 @@ constexpr int f(int i) {
   goto foo;			// { dg-error "goto" }
  foo:
   asm("foo");			// { dg-error "asm" "" { target c++17_down } }
-  int k;			// { dg-error "uninitialized" }
+  int k;			// { dg-error "uninitialized" "" { target c++17_down } }
   A a;				// { dg-error "non-literal" }
   return i;
 }
diff --git gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
index a59bf497b30..93b53273741 100644
--- gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
@@ -3,7 +3,7 @@
 void f(int i)
 {
   [i]() constexpr {
-    int j;			// { dg-error "uninitialized" }
+    int j;			// { dg-error "uninitialized" "" { target c++17_down } }
     j = i;
     return j;
   }();
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
new file mode 100644
index 00000000000..ab7b89da9e8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
@@ -0,0 +1,99 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+// Test basic use.
+
+struct S {
+  int i;
+  constexpr S(bool b) {
+    if (b)
+      i = 42;
+  }
+};
+constexpr S s1(true);
+constexpr S s2(false); // { dg-error "not a constant expression" }
+
+constexpr int
+fn1 (int x)
+{
+  int a;
+  a = 5;
+  return x + a;
+}
+
+static_assert (fn1 (2) == 7);
+
+constexpr int
+fn2 (int x)
+{
+  const int a; // { dg-error "uninitialized .const a." }
+  constexpr int b; // { dg-error "uninitialized .const b." }
+  return x;
+}
+
+constexpr int
+fn3 (int x)
+{
+  int a; // { dg-message ".int a. is not const" }
+  return x + a; // { dg-error "the value of .a. is not usable in a constant expression" }
+}
+
+constexpr int a = fn3 (5); // { dg-message "in .constexpr. expansion of" }
+
+constexpr int
+fn4 ()
+{
+  struct S { int a = -5; int b; } s;
+  return s.a;
+}
+
+static_assert (fn4 () == -5);
+
+constexpr int
+fn5 ()
+{
+  struct S { int a = 9; int b; } s;
+  return s.b;
+}
+
+constexpr int b = fn5 (); // { dg-error "accessing uninitialized member" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+
+constexpr int
+fn6 ()
+{
+  int a;
+  return 42;
+}
+
+static_assert (fn6 () == 42);
+
+constexpr int
+fn7 (bool b)
+{
+  int a; // { dg-message ".int a. is not const" }
+  if (b)
+    a = 42;
+  return a;
+}
+
+static_assert (fn7 (true) == 42);
+static_assert (fn7 (false) == 42); // { dg-error "non-constant condition|the value of .a. is not usable" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+
+constexpr int
+fn8 (int n)
+{
+  int r;
+  switch (n)
+    {
+    case 1:
+    r = n;
+    return r;
+    case 42:
+    r = n;
+    return r;
+    }
+}
+
+static_assert (fn8 (1) == 1);
+static_assert (fn8 (42) == 42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C
new file mode 100644
index 00000000000..74bf8e6677b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C
@@ -0,0 +1,11 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+// In c++2a we don't emit a call to _ZN3FooI3ArgEC1Ev.
+
+struct Arg;
+struct Base {
+  int i;
+  virtual ~Base();
+};
+template <class> struct Foo : Base { };
+Foo<Arg> a;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C
new file mode 100644
index 00000000000..1c7836a674a
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C
@@ -0,0 +1,16 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int i;
+  constexpr S(int) : i(10) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C
new file mode 100644
index 00000000000..7d3d3729b31
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C
@@ -0,0 +1,16 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int uninit;
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42); // { dg-error "not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C
new file mode 100644
index 00000000000..3d4460a0eb8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C
@@ -0,0 +1,37 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct E { };
+
+struct S {
+  E e;
+  constexpr S() {}
+};
+
+constexpr S s;
+constexpr S s2[4];
+
+struct W {
+  [[no_unique_address]] E e1, e2;
+  constexpr W() {}
+};
+
+constexpr W w;
+constexpr W w2[4];
+
+struct Y {
+  [[no_unique_address]] E e;
+  __extension__ char a[0];
+  constexpr Y() {}
+};
+
+constexpr Y y;
+constexpr Y y2[4];
+
+struct Z {
+  [[no_unique_address]] E e;
+  int i;
+  constexpr Z(int n) :i(n) { }
+};
+
+constexpr Z z(42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C
new file mode 100644
index 00000000000..6ab6abf1505
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C
@@ -0,0 +1,28 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct E {
+  constexpr E() = default;
+  constexpr E(int) {}
+};
+
+struct W {
+  [[no_unique_address]] E e;
+  constexpr W(int) : e(8) {}
+};
+
+constexpr W w = W(42);
+
+struct S {
+  E e;
+  constexpr S() : e{} { }
+};
+
+constexpr S s;
+
+struct S2 {
+  [[no_unique_address]] E e;
+  constexpr S2() : e{} { }
+};
+
+constexpr S2 s2;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init15.C gcc/testsuite/g++.dg/cpp2a/constexpr-init15.C
new file mode 100644
index 00000000000..f80d3f2c27c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init15.C
@@ -0,0 +1,31 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct E {
+  constexpr E() = default;
+  constexpr E(int) {}
+};
+
+struct W {
+  [[no_unique_address]] E e;
+  int i;
+  constexpr W(int) : e(8), i(11) {}
+};
+
+constexpr W w = W(42);
+
+struct S {
+  E e;
+  int i;
+  constexpr S() : e{}, i(11) { }
+};
+
+constexpr S s;
+
+struct S2 {
+  [[no_unique_address]] E e;
+  int i;
+  constexpr S2() : e{}, i(11) { }
+};
+
+constexpr S2 s2;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C
new file mode 100644
index 00000000000..541da1c023f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C
@@ -0,0 +1,15 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+  int i;
+  constexpr A() : i{} {} 
+};
+
+struct B
+{
+  A a;
+};
+
+constexpr B b[] = { {} };
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C
new file mode 100644
index 00000000000..dd2735289cb
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C
@@ -0,0 +1,16 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+  int i;
+  constexpr A() {} 
+};
+
+struct B
+{
+  A a;
+};
+
+// A::i not initialized.
+constexpr B b[] = { {} }; // { dg-error "is not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C
new file mode 100644
index 00000000000..dd614ede2c6
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C
@@ -0,0 +1,61 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+// This bullet in [dcl.constexpr] is now gone:
+//  - every non-static data member and base class sub-object shall be initialized
+
+struct A {
+  int i;
+  constexpr A(int _i) { i = _i; }
+};
+
+struct B {
+  int i;
+  constexpr B() { }
+};
+
+// Anonymous members.
+struct E {
+  int a;
+  union {
+    char b;
+    __extension__ struct {
+      double c;
+      long d;
+    };  
+    union {
+      char e;
+      void *f; 
+    };  
+  };  
+  __extension__ struct {
+    long long g;
+    __extension__ struct {
+      int h;
+      double i;
+    };  
+    union {
+      char *j; 
+      E *k; 
+    };  
+  };  
+
+  // Completely initialized.
+  constexpr E(int(&)[1]) : a(), b(), g(), h(), i(), j() {}
+  constexpr E(int(&)[3]) : a(), e(), g(), h(), i(), k() {}
+  constexpr E(int(&)[7]) : a(), b(), g(), h(), i(), j() {}
+  constexpr E(int(&)[8]) : a(), f(), g(), h(), i(), k() {}
+  constexpr E(int(&)[9]) : a(), c(), d(), g(), h(), i(), k() {}
+
+  // Missing d, i, j/k union init.
+  constexpr E(int(&)[2]) : a(), c(), g(), h() {}
+
+  // Missing h, j/k union init.
+  constexpr E(int(&)[4]) : a(), c(), d(), g(), i() {}
+
+  // Missing b/c/d/e/f union init.
+  constexpr E(int(&)[5]) : a(), g(), h(), i(), k() {}
+
+  // Missing a, b/c/d/e/f union, g/h/i/j/k struct init.
+  constexpr E(int(&)[6]) {}
+};
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C
new file mode 100644
index 00000000000..0d21f26da0e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C
@@ -0,0 +1,22 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S { int i; };
+
+constexpr void
+fn ()
+{
+  S s;
+
+  []() constexpr {
+    int i;
+  }();
+}
+
+constexpr int
+fn2 ()
+{
+  return __extension__ ({ int n; n; }); // { dg-error "not usable in a constant expression" }
+}
+
+constexpr int i = fn2 (); // { dg-message "in .constexpr. expansion of" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C
new file mode 100644
index 00000000000..a2994f5272c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C
@@ -0,0 +1,26 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+/* We used to get the "constexpr constructor for union S::<unnamed union>
+   must initialize exactly one non-static data member" error, but not anymore
+   in C++20.  */
+
+struct S {
+  union {
+    int i;
+    double d;
+  };
+  constexpr S() { }
+};
+
+union U {
+  int a;
+  constexpr U() { }
+};
+
+struct W {
+  union {
+    int a;
+  };
+  constexpr W() { }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C
new file mode 100644
index 00000000000..dd2741efa8c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C
@@ -0,0 +1,63 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int a = 1;
+  constexpr S() = default;
+};
+
+constexpr S s;
+
+union U {
+  int a = 1;
+  constexpr U() = default;
+};
+
+constexpr U u;
+
+struct S2 {
+  int a;
+  constexpr S2() = default;
+};
+
+constexpr S2 s2; // { dg-error "uninitialized .const s2." }
+
+union U2 {
+  int a;
+  constexpr U2() = default;
+};
+
+constexpr U2 u2; // { dg-error "uninitialized .const u2." }
+
+struct S3 {
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int a;
+  } u;
+  constexpr S3() = default;
+};
+
+constexpr S3 s3; // { dg-error "uninitialized .const s3." }
+
+struct S4 {
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int n;
+  } u;
+  constexpr S4() = default;
+};
+
+constexpr S4 s4; // { dg-error "uninitialized .const s4." }
+
+struct S5 {
+  union {
+    int n = 0;
+  };
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int m;
+  } u;
+  constexpr S5() = default;
+};
+
+constexpr S5 s5; // { dg-error "uninitialized .const s5.|not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C
new file mode 100644
index 00000000000..0d5a4a79c90
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C
@@ -0,0 +1,15 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C
new file mode 100644
index 00000000000..b44098cc89b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C
@@ -0,0 +1,17 @@
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int (&)[8]) : W(8) { }
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+int arr[8];
+constexpr auto a = W(arr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
index 47cdce88e36..3b51bf7c901 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
@@ -4,13 +4,13 @@
 
 constexpr int foo ()
 try {			// { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } }
-  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" }
+  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
   static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
   goto l;		// { dg-error "'goto' in 'constexpr' function" }
   l:;
   return 0;
 } catch (...) {
-  long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" }
+  long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
   static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function" }
   goto l2;		// { dg-error "'goto' in 'constexpr' function" }
   l2:;
@@ -19,19 +19,19 @@ try {			// { dg-warning "function-try-block body of 'constexpr' function only av
 
 constexpr int bar ()
 {
-  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" }
+  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
   static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
   goto l;		// { dg-error "'goto' in 'constexpr' function" }
   l:;
   try {			// { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } }
-    short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" }
+    short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
     static float d;	// { dg-error "'d' declared 'static' in 'constexpr' function" }
-			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target *-*-* } .-1 }
+			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target c++17_down } .-1 }
     goto l2;		// { dg-error "'goto' in 'constexpr' function" }
     l2:;
     return 0;
   } catch (int) {
-    char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" }
+    char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" "" { target c++17_down } }
     static int f = 5;	// { dg-error "'f' declared 'static' in 'constexpr' function" }
     goto l3;		// { dg-error "'goto' in 'constexpr' function" }
     l3:;
diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 389b25e16ea..c86aead79af 100644
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -134,8 +134,8 @@
 
 #ifndef __cpp_constexpr
 #  error "__cpp_constexpr"
-#elif __cpp_constexpr != 201603
-#  error "__cpp_constexpr != 201603"
+#elif __cpp_constexpr != 201907
+#  error "__cpp_constexpr != 201907"
 #endif
 
 #ifndef __cpp_decltype_auto
diff --git gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C
new file mode 100644
index 00000000000..8ee9b0327a5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C
@@ -0,0 +1,15 @@
+// Test lambda mangling
+// { dg-do compile { target c++2a } }
+// { dg-require-weak "" }
+// { dg-options "-fno-inline" }
+
+template<typename T> struct R {
+  static int x;
+};
+// "int i;" makes the op() non-constexpr in C++17.  In C++20, it does not.
+template<typename T> int R<T>::x = []{int i; return 1;}();
+template int R<int>::x;
+// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
+// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
+// { dg-final { scan-assembler-not "_ZNK1RIiE1xMUlvE_clEv" } }
+// { dg-final { scan-assembler-not "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" } }
diff --git gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
index d50df25f693..1139f412fda 100644
--- gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
+++ gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
@@ -1,5 +1,5 @@
 // Origin: PR 44641
-// { dg-do compile }
+// { dg-do compile { target c++17_down } }
 // { dg-options "-gdwarf-2 -O0 -dA" }
 
 template <class A> struct MisplacedDbg;
@@ -40,3 +40,11 @@ struct MisplacedDbg  // { dg-function-on-line {_ZN12MisplacedDbgI3ArgEC[12]Ev} {
 static MisplacedDbg<Arg> static_var1;
 static MisplacedDbg<Arg*> static_var2;
 static MisplacedDbg<Full> static_var3;
+
+// This test is skipped in C++20 because we consider the default constructor
+// MisplacedDbg() constexpr despite the uninitialized member "int i;".  So
+// the calls to
+//    MisplacedDbg<Arg>::MisplacedDbg()
+//    MisplacedDbg<Full>::MisplacedDbg()
+//    MisplacedDbg<Arg*>::MisplacedDbg()
+// are elided.  (This comment is here not to mess up the line numbers.)
diff --git gcc/testsuite/g++.dg/ext/stmtexpr21.C gcc/testsuite/g++.dg/ext/stmtexpr21.C
index 259cb2f1913..97052a117a7 100644
--- gcc/testsuite/g++.dg/ext/stmtexpr21.C
+++ gcc/testsuite/g++.dg/ext/stmtexpr21.C
@@ -7,7 +7,7 @@ struct test { const int *addr; };
 const test* setup()
 {
   static constexpr test atest =
-    { ({ int inner; (const int*)(0); }) };  // { dg-error "uninitialized" }
+    { ({ int inner; (const int*)(0); }) };  // { dg-error "uninitialized" "" { target c++17_down } }
 
   return &atest;
 }
Jason Merrill Dec. 5, 2019, 7:52 p.m. UTC | #20
On 12/5/19 2:28 PM, Marek Polacek wrote:
> On Thu, Dec 05, 2019 at 01:37:45PM -0500, Jason Merrill wrote:
>> On 12/4/19 5:43 PM, Marek Polacek wrote:
>>> @@ -2222,12 +2243,24 @@ reduced_constant_expression_p (tree t)
>>>    	    return false;
>>>    	  if (field)
>>>    	    {
>>> +	  retry:
>>>    	      if (idx != field)
>>> -		return false;
>>> +		{
>>> +		  /* Empty class field may or may not have an initializer.  */
>>> +		  if (is_really_empty_class (TREE_TYPE (field),
>>> +					     /*ignore_vptr*/false))
>>> +		    {
>>> +		      field = next_initializable_field (DECL_CHAIN (field));
>>> +		      goto retry;
>>> +		    }
>>> +		  else
>>> +		    return false;
>>> +		}
>>
>> I'm not as anti-goto as many people, but it would be shorter to write this
>> as a loop:
>>
>> for (; idx != field;
>>       field = next_initializable_field (DECL_CHAIN (field))
>>    if (!is_really_empty_class (TREE_TYPE (field), false))
>>      return false;
> 
> Works for me!  Adjusted ...
> 
>>>    	      field = next_initializable_field (DECL_CHAIN (field));
>>>    	    }
>>>    	}
>>> -      if (field)
>>> +      if (field && !is_really_empty_class (TREE_TYPE (field),
>>> +					   /*ignore_vptr*/false))
>>>    	return false;
>>
>> And I think we should loop here, too, in case there's a non-empty field at
>> the end.
> 
> ... and here too, thanks.  No other changes otherwise.
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?

OK, thanks.

> 2019-12-05  Marek Polacek  <polacek@redhat.com>
> 	    Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> 	* c-cppbuiltin.c (c_cpp_builtins): Adjust the value of __cpp_constexpr.
> 
> 	* class.c (trivial_default_constructor_is_constexpr): Return true in
> 	C++20.
> 	* constexpr.c (cx_check_missing_mem_inits): Allow missing field
> 	initializers in C++20.
> 	(cxx_eval_call_expression): Don't clear CONSTRUCTOR_NO_CLEARING for
> 	constexpr constructors in C++20.
> 	(reduced_constant_expression_p): Don't set FIELD for union and array
> 	types.  Skip empty class fields without initializers.
> 	* decl.c (check_for_uninitialized_const_var): Permit trivial default
> 	initialization in constexpr.
> 	(next_initializable_field): Don't skip vptr fields.
> 	* method.c (walk_field_subobs): Still consider a constructor that
> 	doesn't initialize all the members constexpr.
> 
> 	* g++.dg/cpp0x/constexpr-array6.C: Adjust dg-error.
> 	* g++.dg/cpp0x/constexpr-ctor.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-diag3.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-diag4.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-ex3.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-template2.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-union2.C: Likewise.
> 	* g++.dg/cpp0x/lambda/lambda-mangle.C: Rip out a piece of code ...
> 	* g++.dg/cpp0x/lambda/lambda-mangle6.C: ... and put it here.
> 	* g++.dg/cpp0x/pr79118.C: Adjust dg-error.
> 	* g++.dg/cpp1y/constexpr-83921-3.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-neg1.C: Likewise.
> 	* g++.dg/cpp1z/constexpr-lambda12.C: Likewise.
> 	* g++.dg/cpp1z/feat-cxx1z.C: Use -std=c++17.
> 	* g++.dg/cpp2a/constexpr-init1.C: New test.
> 	* g++.dg/cpp2a/constexpr-init2.C: New test.
> 	* g++.dg/cpp2a/constexpr-init3.C: New test.
> 	* g++.dg/cpp2a/constexpr-init4.C: New test.
> 	* g++.dg/cpp2a/constexpr-init5.C: New test.
> 	* g++.dg/cpp2a/constexpr-init6.C: New test.
> 	* g++.dg/cpp2a/constexpr-init7.C: New test.
> 	* g++.dg/cpp2a/constexpr-init8.C: New test.
> 	* g++.dg/cpp2a/constexpr-init9.C: New test.
> 	* g++.dg/cpp2a/constexpr-init10.C: New test.
> 	* g++.dg/cpp2a/constexpr-init11.C: New test.
> 	* g++.dg/cpp2a/constexpr-init12.C: New test.
> 	* g++.dg/cpp2a/constexpr-init13.C: New test.
> 	* g++.dg/cpp2a/constexpr-init14.C: New test.
> 	* g++.dg/cpp2a/constexpr-init15.C: New test.
> 	* g++.dg/cpp2a/constexpr-try5.C: Adjust dg-error.
> 	* g++.dg/cpp2a/feat-cxx2a.C: Test __cpp_constexpr.
> 	* g++.dg/cpp2a/lambda-mangle.C: New test.
> 	* g++.dg/debug/dwarf2/pr44641.C: Skip for c++2a.
> 	* g++.dg/ext/stmtexpr21.C: Adjust dg-error.
> 
> diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
> index c7f4659456a..5ad626d7f24 100644
> --- gcc/c-family/c-cppbuiltin.c
> +++ gcc/c-family/c-cppbuiltin.c
> @@ -975,7 +975,8 @@ c_cpp_builtins (cpp_reader *pfile)
>   	  cpp_define (pfile, "__cpp_fold_expressions=201603L");
>   	  cpp_define (pfile, "__cpp_nontype_template_args=201411L");
>   	  cpp_define (pfile, "__cpp_range_based_for=201603L");
> -	  cpp_define (pfile, "__cpp_constexpr=201603L");
> +	  if (cxx_dialect <= cxx17)
> +	    cpp_define (pfile, "__cpp_constexpr=201603L");
>   	  cpp_define (pfile, "__cpp_if_constexpr=201606L");
>   	  cpp_define (pfile, "__cpp_capture_star_this=201603L");
>   	  cpp_define (pfile, "__cpp_inline_variables=201606L");
> @@ -997,6 +998,7 @@ c_cpp_builtins (cpp_reader *pfile)
>   	  cpp_define (pfile, "__cpp_init_captures=201803L");
>   	  cpp_define (pfile, "__cpp_generic_lambdas=201707L");
>   	  cpp_define (pfile, "__cpp_designated_initializers=201707L");
> +	  cpp_define (pfile, "__cpp_constexpr=201907L");
>   	  cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
>   	  cpp_define (pfile, "__cpp_conditional_explicit=201806L");
>   	  cpp_define (pfile, "__cpp_consteval=201811L");
> diff --git gcc/cp/class.c gcc/cp/class.c
> index f36f75fa0db..d8bb44990b7 100644
> --- gcc/cp/class.c
> +++ gcc/cp/class.c
> @@ -5288,8 +5288,14 @@ trivial_default_constructor_is_constexpr (tree t)
>     /* A defaulted trivial default constructor is constexpr
>        if there is nothing to initialize.  */
>     gcc_assert (!TYPE_HAS_COMPLEX_DFLT (t));
> -  /* A class with a vptr doesn't have a trivial default ctor.  */
> -  return is_really_empty_class (t, /*ignore_vptr*/true);
> +  /* A class with a vptr doesn't have a trivial default ctor.
> +     In C++20, a class can have transient uninitialized members, e.g.:
> +
> +       struct S { int i; constexpr S() = default; };
> +
> +     should work.  */
> +  return (cxx_dialect >= cxx2a
> +	  || is_really_empty_class (t, /*ignore_vptr*/true));
>   }
>   
>   /* Returns true iff class T has a constexpr default constructor.  */
> diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
> index 370633467ab..19e09c74760 100644
> --- gcc/cp/constexpr.c
> +++ gcc/cp/constexpr.c
> @@ -767,6 +767,10 @@ massage_constexpr_body (tree fun, tree body)
>   static bool
>   cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
>   {
> +  /* We allow uninitialized bases/fields in C++20.  */
> +  if (cxx_dialect >= cxx2a)
> +    return false;
> +
>     unsigned nelts = 0;
>     
>     if (body)
> @@ -815,7 +819,7 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
>   	    continue;
>   	  if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
>   	    {
> -	      /* Recurse to check the anonummous aggregate member.  */
> +	      /* Recurse to check the anonymous aggregate member.  */
>   	      bad |= cx_check_missing_mem_inits
>   		(TREE_TYPE (field), NULL_TREE, complain);
>   	      if (bad && !complain)
> @@ -2179,15 +2183,26 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
>   	entry->result = result;
>       }
>   
> -  /* The result of a constexpr function must be completely initialized.  */
> -  if (TREE_CODE (result) == CONSTRUCTOR)
> +  /* The result of a constexpr function must be completely initialized.
> +
> +     However, in C++20, a constexpr constructor doesn't necessarily have
> +     to initialize all the fields, so we don't clear CONSTRUCTOR_NO_CLEARING
> +     in order to detect reading an unitialized object in constexpr instead
> +     of value-initializing it.  (reduced_constant_expression_p is expected to
> +     take care of clearing the flag.)  */
> +  if (TREE_CODE (result) == CONSTRUCTOR
> +      && (cxx_dialect < cxx2a
> +	  || !DECL_CONSTRUCTOR_P (fun)))
>       clear_no_implicit_zero (result);
>   
>     pop_cx_call_context ();
>     return result;
>   }
>   
> -/* FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
> +/* Return true if T is a valid constant initializer.  If a CONSTRUCTOR
> +   initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
> +   cleared.
> +   FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
>   
>   bool
>   reduced_constant_expression_p (tree t)
> @@ -2209,6 +2224,12 @@ reduced_constant_expression_p (tree t)
>   	  if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
>   	    /* An initialized vector would have a VECTOR_CST.  */
>   	    return false;
> +	  else if (cxx_dialect >= cxx2a
> +		   /* An ARRAY_TYPE doesn't have any TYPE_FIELDS.  */
> +		   && (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
> +		       /* A union only initializes one member.  */
> +		       || TREE_CODE (TREE_TYPE (t)) == UNION_TYPE))
> +	    field = NULL_TREE;
>   	  else
>   	    field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)));
>   	}
> @@ -2222,14 +2243,20 @@ reduced_constant_expression_p (tree t)
>   	    return false;
>   	  if (field)
>   	    {
> -	      if (idx != field)
> -		return false;
> +	      /* Empty class field may or may not have an initializer.  */
> +	      for (; idx != field;
> +		   field = next_initializable_field (DECL_CHAIN (field)))
> +		if (!is_really_empty_class (TREE_TYPE (field),
> +					    /*ignore_vptr*/false))
> +		  return false;
>   	      field = next_initializable_field (DECL_CHAIN (field));
>   	    }
>   	}
> -      if (field)
> -	return false;
> -      else if (CONSTRUCTOR_NO_CLEARING (t))
> +      /* There could be a non-empty field at the end.  */
> +      for (; field; field = next_initializable_field (DECL_CHAIN (field)))
> +	if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/false))
> +	  return false;
> +      if (CONSTRUCTOR_NO_CLEARING (t))
>   	/* All the fields are initialized.  */
>   	CONSTRUCTOR_NO_CLEARING (t) = false;
>         return true;
> diff --git gcc/cp/decl.c gcc/cp/decl.c
> index 7897327ad9a..a44f1721ea2 100644
> --- gcc/cp/decl.c
> +++ gcc/cp/decl.c
> @@ -5858,8 +5858,12 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
>        7.1.6 */
>     if (VAR_P (decl)
>         && !TYPE_REF_P (type)
> -      && (constexpr_context_p
> -	  || CP_TYPE_CONST_P (type) || var_in_constexpr_fn (decl))
> +      && (CP_TYPE_CONST_P (type)
> +	  /* C++20 permits trivial default initialization in constexpr
> +	     context (P1331R2).  */
> +	  || (cxx_dialect < cxx2a
> +	      && (constexpr_context_p
> +		  || var_in_constexpr_fn (decl))))
>         && !DECL_NONTRIVIALLY_INITIALIZED_P (decl))
>       {
>         tree field = default_init_uninitialized_part (type);
> @@ -5868,7 +5872,7 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
>   
>         bool show_notes = true;
>   
> -      if (!constexpr_context_p)
> +      if (!constexpr_context_p || cxx_dialect >= cxx2a)
>   	{
>   	  if (CP_TYPE_CONST_P (type))
>   	    {
> @@ -5938,7 +5942,11 @@ next_initializable_field (tree field)
>   	 && (TREE_CODE (field) != FIELD_DECL
>   	     || DECL_UNNAMED_BIT_FIELD (field)
>   	     || (DECL_ARTIFICIAL (field)
> -		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field)))))
> +		 /* In C++17, don't skip base class fields.  */
> +		 && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field))
> +		 /* Don't skip vptr fields.  We might see them when we're
> +		    called from reduced_constant_expression_p.  */
> +		 && !DECL_VIRTUAL_P (field))))
>       field = DECL_CHAIN (field);
>   
>     return field;
> diff --git gcc/cp/method.c gcc/cp/method.c
> index a707940cacc..d2aed473d77 100644
> --- gcc/cp/method.c
> +++ gcc/cp/method.c
> @@ -1985,10 +1985,12 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname,
>   	  if (bad && deleted_p)
>   	    *deleted_p = true;
>   
> -	  /* For an implicitly-defined default constructor to be constexpr,
> -	     every member must have a user-provided default constructor or
> -	     an explicit initializer.  */
> -	  if (constexpr_p && !CLASS_TYPE_P (mem_type)
> +	  /* Before C++20, for an implicitly-defined default constructor to
> +	     be constexpr, every member must have a user-provided default
> +	     constructor or an explicit initializer.  */
> +	  if (constexpr_p
> +	      && cxx_dialect < cxx2a
> +	      && !CLASS_TYPE_P (mem_type)
>   	      && TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE)
>   	    {
>   	      *constexpr_p = false;
> diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
> index 16eacdde440..48dae5d9609 100644
> --- gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
> @@ -4,7 +4,7 @@
>   struct A
>   {
>     int i;
> -  constexpr A() {}		// { dg-error "A::i" }
> +  constexpr A() {}		// { dg-error "A::i" "" { target c++17_down } }
>   };
>   
>   struct B
> @@ -12,4 +12,5 @@ struct B
>     A a;
>   };
>   
> -constexpr B b[] = { {} };	// { dg-error "A::A" }
> +constexpr B b[] = { {} };	// { dg-error "A::A" "" { target c++17_down } }
> +// { dg-error "is not a constant expression" "" { target c++2a } .-1 }
> diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
> index 55beda7c49f..1d0fa479cbc 100644
> --- gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
> @@ -3,5 +3,5 @@
>   struct A
>   {
>     int i;
> -  constexpr A() { }		// { dg-error "A::i" }
> +  constexpr A() { }		// { dg-error "A::i" "" { target c++17_down } }
>   };
> diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
> index e54b26c7f6a..1c43569615c 100644
> --- gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
> @@ -48,7 +48,7 @@ struct Def
>   {
>     int _M_i;			// { dg-message "does not initialize" }
>   
> -  constexpr Def() = default;	// { dg-error "implicit declaration is not .constexpr." }
> +  constexpr Def() = default;	// { dg-error "implicit declaration is not .constexpr." "" { target c++17_down } }
>   };
>   
>   constexpr Def defobj;		// { dg-error "uninitialized" }
> diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
> index 13ca6fa2390..c603bdd1a00 100644
> --- gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
> @@ -21,5 +21,5 @@ struct A1
>   struct B1
>   {
>       A1 a1;
> -    constexpr B1() {} // { dg-error "B1::a1" }
> +    constexpr B1() {} // { dg-error "B1::a1" "" { target c++17_down } }
>   };
> diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
> index a5893563eec..9d6d5ff587c 100644
> --- gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
> @@ -6,7 +6,7 @@
>   struct A
>   {
>     int i;
> -  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" }
> +  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" "" { target c++17_down } }
>   };
>   
>   template <class T>
> diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
> index 12a8d42b31f..71eb559a24c 100644
> --- gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
> @@ -3,7 +3,7 @@
>   template <class T> struct A
>   {
>     T t;
> -  constexpr A() { }		// { dg-error "::t" }
> +  constexpr A() { }		// { dg-error "::t" "" { target c++17_down } }
>   };
>   
>   int main()
> diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
> index 1a5e832ac34..c22ecc99efb 100644
> --- gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
> @@ -14,5 +14,5 @@ union bar
>     int x;
>     short y;
>   
> -  constexpr bar() = default;	// { dg-error "constexpr" }
> +  constexpr bar() = default;	// { dg-error "constexpr" "" { target c++17_down } }
>   };
> diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
> index 15b8b79ecbc..7894ef3051e 100644
> --- gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
> +++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
> @@ -47,17 +47,6 @@ struct S {
>   	 []{return 3;}());
>   };
>   
> -template<typename T> struct R {
> -  static int x;
> -};
> -// "int i;" makes the op() non-constexpr in C++17.
> -template<typename T> int R<T>::x = []{int i; return 1;}();
> -template int R<int>::x;
> -// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
> -// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
> -// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
> -// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
> -
>   void bar()
>   {
>     // lambdas in non-vague linkage functions have internal linkage.
> diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C
> new file mode 100644
> index 00000000000..9ec13e79bbf
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C
> @@ -0,0 +1,15 @@
> +// Test lambda mangling
> +// { dg-do compile { target { c++11 && c++17_down } } }
> +// { dg-require-weak "" }
> +// { dg-options "-fno-inline" }
> +
> +template<typename T> struct R {
> +  static int x;
> +};
> +// "int i;" makes the op() non-constexpr in C++17.  In C++20, it does not.
> +template<typename T> int R<T>::x = []{int i; return 1;}();
> +template int R<int>::x;
> +// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
> +// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
> +// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
> +// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
> diff --git gcc/testsuite/g++.dg/cpp0x/pr79118.C gcc/testsuite/g++.dg/cpp0x/pr79118.C
> index 5db22a96dd4..616b51ea29a 100644
> --- gcc/testsuite/g++.dg/cpp0x/pr79118.C
> +++ gcc/testsuite/g++.dg/cpp0x/pr79118.C
> @@ -13,7 +13,7 @@ struct One
>     constexpr One () : a(), b() {} // { dg-error "multiple" }
>     constexpr One (int) : a() {}
>     constexpr One (unsigned) : b () {}
> -  constexpr One (void *) {} // { dg-error "exactly one" }
> +  constexpr One (void *) {} // { dg-error "exactly one" "" { target c++17_down } }
>   };
>   
>   One a ();
> @@ -30,10 +30,10 @@ struct Two
>     };
>   
>     constexpr Two () : a(), b() {}
> -  constexpr Two (int) : a() {} // { dg-error "b' must be initialized" }
> -  constexpr Two (unsigned) : b () {} // { dg-error "a' must be initialized" }
> -  constexpr Two (void *) {} // { dg-error "a' must be initialized" }
> -   // { dg-error "b' must be initialized" "" { target *-*-* } .-1 }
> +  constexpr Two (int) : a() {} // { dg-error "b' must be initialized" "" { target c++17_down } }
> +  constexpr Two (unsigned) : b () {} // { dg-error "a' must be initialized" "" { target c++17_down } }
> +  constexpr Two (void *) {} // { dg-error "a' must be initialized" "" { target c++17_down } }
> +   // { dg-error "b' must be initialized" "" { target c++17_down } .-1 }
>   };
>   
>   Two e ();
> diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
> index 4b1ed5c3a87..2f1218693e0 100644
> --- gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
> +++ gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
> @@ -2,4 +2,4 @@
>   // { dg-do compile { target c++14 } }
>   
>   struct Foo { int m; };
> -constexpr void test() { Foo f; }  // { dg-error "uninitialized" }
> +constexpr void test() { Foo f; }  // { dg-error "uninitialized" "" { target c++17_down } }
> diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
> index d82dbada1bf..53f0f1f7a2b 100644
> --- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
> +++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
> @@ -8,7 +8,7 @@ constexpr int f(int i) {
>     goto foo;			// { dg-error "goto" }
>    foo:
>     asm("foo");			// { dg-error "asm" "" { target c++17_down } }
> -  int k;			// { dg-error "uninitialized" }
> +  int k;			// { dg-error "uninitialized" "" { target c++17_down } }
>     A a;				// { dg-error "non-literal" }
>     return i;
>   }
> diff --git gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
> index a59bf497b30..93b53273741 100644
> --- gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
> +++ gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
> @@ -3,7 +3,7 @@
>   void f(int i)
>   {
>     [i]() constexpr {
> -    int j;			// { dg-error "uninitialized" }
> +    int j;			// { dg-error "uninitialized" "" { target c++17_down } }
>       j = i;
>       return j;
>     }();
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
> new file mode 100644
> index 00000000000..ab7b89da9e8
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
> @@ -0,0 +1,99 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +// Test basic use.
> +
> +struct S {
> +  int i;
> +  constexpr S(bool b) {
> +    if (b)
> +      i = 42;
> +  }
> +};
> +constexpr S s1(true);
> +constexpr S s2(false); // { dg-error "not a constant expression" }
> +
> +constexpr int
> +fn1 (int x)
> +{
> +  int a;
> +  a = 5;
> +  return x + a;
> +}
> +
> +static_assert (fn1 (2) == 7);
> +
> +constexpr int
> +fn2 (int x)
> +{
> +  const int a; // { dg-error "uninitialized .const a." }
> +  constexpr int b; // { dg-error "uninitialized .const b." }
> +  return x;
> +}
> +
> +constexpr int
> +fn3 (int x)
> +{
> +  int a; // { dg-message ".int a. is not const" }
> +  return x + a; // { dg-error "the value of .a. is not usable in a constant expression" }
> +}
> +
> +constexpr int a = fn3 (5); // { dg-message "in .constexpr. expansion of" }
> +
> +constexpr int
> +fn4 ()
> +{
> +  struct S { int a = -5; int b; } s;
> +  return s.a;
> +}
> +
> +static_assert (fn4 () == -5);
> +
> +constexpr int
> +fn5 ()
> +{
> +  struct S { int a = 9; int b; } s;
> +  return s.b;
> +}
> +
> +constexpr int b = fn5 (); // { dg-error "accessing uninitialized member" }
> +// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
> +
> +constexpr int
> +fn6 ()
> +{
> +  int a;
> +  return 42;
> +}
> +
> +static_assert (fn6 () == 42);
> +
> +constexpr int
> +fn7 (bool b)
> +{
> +  int a; // { dg-message ".int a. is not const" }
> +  if (b)
> +    a = 42;
> +  return a;
> +}
> +
> +static_assert (fn7 (true) == 42);
> +static_assert (fn7 (false) == 42); // { dg-error "non-constant condition|the value of .a. is not usable" }
> +// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
> +
> +constexpr int
> +fn8 (int n)
> +{
> +  int r;
> +  switch (n)
> +    {
> +    case 1:
> +    r = n;
> +    return r;
> +    case 42:
> +    r = n;
> +    return r;
> +    }
> +}
> +
> +static_assert (fn8 (1) == 1);
> +static_assert (fn8 (42) == 42);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C
> new file mode 100644
> index 00000000000..74bf8e6677b
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C
> @@ -0,0 +1,11 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +// In c++2a we don't emit a call to _ZN3FooI3ArgEC1Ev.
> +
> +struct Arg;
> +struct Base {
> +  int i;
> +  virtual ~Base();
> +};
> +template <class> struct Foo : Base { };
> +Foo<Arg> a;
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C
> new file mode 100644
> index 00000000000..1c7836a674a
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C
> @@ -0,0 +1,16 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +struct S {
> +  int i;
> +  constexpr S(int) : i(10) {}
> +};
> +
> +struct W {
> +  constexpr W(int) : s(8), p() {}
> +
> +  S s;
> +  int *p;
> +};
> +
> +constexpr auto a = W(42);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C
> new file mode 100644
> index 00000000000..7d3d3729b31
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C
> @@ -0,0 +1,16 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +struct S {
> +  int uninit;
> +  constexpr S(int) {}
> +};
> +
> +struct W {
> +  constexpr W(int) : s(8), p() {}
> +
> +  S s;
> +  int *p;
> +};
> +
> +constexpr auto a = W(42); // { dg-error "not a constant expression" }
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C
> new file mode 100644
> index 00000000000..3d4460a0eb8
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C
> @@ -0,0 +1,37 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +struct E { };
> +
> +struct S {
> +  E e;
> +  constexpr S() {}
> +};
> +
> +constexpr S s;
> +constexpr S s2[4];
> +
> +struct W {
> +  [[no_unique_address]] E e1, e2;
> +  constexpr W() {}
> +};
> +
> +constexpr W w;
> +constexpr W w2[4];
> +
> +struct Y {
> +  [[no_unique_address]] E e;
> +  __extension__ char a[0];
> +  constexpr Y() {}
> +};
> +
> +constexpr Y y;
> +constexpr Y y2[4];
> +
> +struct Z {
> +  [[no_unique_address]] E e;
> +  int i;
> +  constexpr Z(int n) :i(n) { }
> +};
> +
> +constexpr Z z(42);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C
> new file mode 100644
> index 00000000000..6ab6abf1505
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C
> @@ -0,0 +1,28 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +struct E {
> +  constexpr E() = default;
> +  constexpr E(int) {}
> +};
> +
> +struct W {
> +  [[no_unique_address]] E e;
> +  constexpr W(int) : e(8) {}
> +};
> +
> +constexpr W w = W(42);
> +
> +struct S {
> +  E e;
> +  constexpr S() : e{} { }
> +};
> +
> +constexpr S s;
> +
> +struct S2 {
> +  [[no_unique_address]] E e;
> +  constexpr S2() : e{} { }
> +};
> +
> +constexpr S2 s2;
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init15.C gcc/testsuite/g++.dg/cpp2a/constexpr-init15.C
> new file mode 100644
> index 00000000000..f80d3f2c27c
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init15.C
> @@ -0,0 +1,31 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +struct E {
> +  constexpr E() = default;
> +  constexpr E(int) {}
> +};
> +
> +struct W {
> +  [[no_unique_address]] E e;
> +  int i;
> +  constexpr W(int) : e(8), i(11) {}
> +};
> +
> +constexpr W w = W(42);
> +
> +struct S {
> +  E e;
> +  int i;
> +  constexpr S() : e{}, i(11) { }
> +};
> +
> +constexpr S s;
> +
> +struct S2 {
> +  [[no_unique_address]] E e;
> +  int i;
> +  constexpr S2() : e{}, i(11) { }
> +};
> +
> +constexpr S2 s2;
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C
> new file mode 100644
> index 00000000000..541da1c023f
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C
> @@ -0,0 +1,15 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +struct A
> +{
> +  int i;
> +  constexpr A() : i{} {}
> +};
> +
> +struct B
> +{
> +  A a;
> +};
> +
> +constexpr B b[] = { {} };
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C
> new file mode 100644
> index 00000000000..dd2735289cb
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C
> @@ -0,0 +1,16 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +struct A
> +{
> +  int i;
> +  constexpr A() {}
> +};
> +
> +struct B
> +{
> +  A a;
> +};
> +
> +// A::i not initialized.
> +constexpr B b[] = { {} }; // { dg-error "is not a constant expression" }
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C
> new file mode 100644
> index 00000000000..dd614ede2c6
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C
> @@ -0,0 +1,61 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +// This bullet in [dcl.constexpr] is now gone:
> +//  - every non-static data member and base class sub-object shall be initialized
> +
> +struct A {
> +  int i;
> +  constexpr A(int _i) { i = _i; }
> +};
> +
> +struct B {
> +  int i;
> +  constexpr B() { }
> +};
> +
> +// Anonymous members.
> +struct E {
> +  int a;
> +  union {
> +    char b;
> +    __extension__ struct {
> +      double c;
> +      long d;
> +    };
> +    union {
> +      char e;
> +      void *f;
> +    };
> +  };
> +  __extension__ struct {
> +    long long g;
> +    __extension__ struct {
> +      int h;
> +      double i;
> +    };
> +    union {
> +      char *j;
> +      E *k;
> +    };
> +  };
> +
> +  // Completely initialized.
> +  constexpr E(int(&)[1]) : a(), b(), g(), h(), i(), j() {}
> +  constexpr E(int(&)[3]) : a(), e(), g(), h(), i(), k() {}
> +  constexpr E(int(&)[7]) : a(), b(), g(), h(), i(), j() {}
> +  constexpr E(int(&)[8]) : a(), f(), g(), h(), i(), k() {}
> +  constexpr E(int(&)[9]) : a(), c(), d(), g(), h(), i(), k() {}
> +
> +  // Missing d, i, j/k union init.
> +  constexpr E(int(&)[2]) : a(), c(), g(), h() {}
> +
> +  // Missing h, j/k union init.
> +  constexpr E(int(&)[4]) : a(), c(), d(), g(), i() {}
> +
> +  // Missing b/c/d/e/f union init.
> +  constexpr E(int(&)[5]) : a(), g(), h(), i(), k() {}
> +
> +  // Missing a, b/c/d/e/f union, g/h/i/j/k struct init.
> +  constexpr E(int(&)[6]) {}
> +};
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C
> new file mode 100644
> index 00000000000..0d21f26da0e
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C
> @@ -0,0 +1,22 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +struct S { int i; };
> +
> +constexpr void
> +fn ()
> +{
> +  S s;
> +
> +  []() constexpr {
> +    int i;
> +  }();
> +}
> +
> +constexpr int
> +fn2 ()
> +{
> +  return __extension__ ({ int n; n; }); // { dg-error "not usable in a constant expression" }
> +}
> +
> +constexpr int i = fn2 (); // { dg-message "in .constexpr. expansion of" }
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C
> new file mode 100644
> index 00000000000..a2994f5272c
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C
> @@ -0,0 +1,26 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +/* We used to get the "constexpr constructor for union S::<unnamed union>
> +   must initialize exactly one non-static data member" error, but not anymore
> +   in C++20.  */
> +
> +struct S {
> +  union {
> +    int i;
> +    double d;
> +  };
> +  constexpr S() { }
> +};
> +
> +union U {
> +  int a;
> +  constexpr U() { }
> +};
> +
> +struct W {
> +  union {
> +    int a;
> +  };
> +  constexpr W() { }
> +};
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C
> new file mode 100644
> index 00000000000..dd2741efa8c
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C
> @@ -0,0 +1,63 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +struct S {
> +  int a = 1;
> +  constexpr S() = default;
> +};
> +
> +constexpr S s;
> +
> +union U {
> +  int a = 1;
> +  constexpr U() = default;
> +};
> +
> +constexpr U u;
> +
> +struct S2 {
> +  int a;
> +  constexpr S2() = default;
> +};
> +
> +constexpr S2 s2; // { dg-error "uninitialized .const s2." }
> +
> +union U2 {
> +  int a;
> +  constexpr U2() = default;
> +};
> +
> +constexpr U2 u2; // { dg-error "uninitialized .const u2." }
> +
> +struct S3 {
> +  // FIXME if it's anonymous union, we don't give the error below
> +  union {
> +    int a;
> +  } u;
> +  constexpr S3() = default;
> +};
> +
> +constexpr S3 s3; // { dg-error "uninitialized .const s3." }
> +
> +struct S4 {
> +  // FIXME if it's anonymous union, we don't give the error below
> +  union {
> +    int n;
> +  } u;
> +  constexpr S4() = default;
> +};
> +
> +constexpr S4 s4; // { dg-error "uninitialized .const s4." }
> +
> +struct S5 {
> +  union {
> +    int n = 0;
> +  };
> +  // FIXME if it's anonymous union, we don't give the error below
> +  union {
> +    int m;
> +  } u;
> +  constexpr S5() = default;
> +};
> +
> +constexpr S5 s5; // { dg-error "uninitialized .const s5.|not a constant expression" }
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C
> new file mode 100644
> index 00000000000..0d5a4a79c90
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C
> @@ -0,0 +1,15 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +struct S {
> +  constexpr S(int) {}
> +};
> +
> +struct W {
> +  constexpr W(int) : s(8), p() {}
> +
> +  S s;
> +  int *p;
> +};
> +
> +constexpr auto a = W(42);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C
> new file mode 100644
> index 00000000000..b44098cc89b
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C
> @@ -0,0 +1,17 @@
> +// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
> +// { dg-do compile { target c++2a } }
> +
> +struct S {
> +  constexpr S(int) {}
> +};
> +
> +struct W {
> +  constexpr W(int (&)[8]) : W(8) { }
> +  constexpr W(int) : s(8), p() {}
> +
> +  S s;
> +  int *p;
> +};
> +
> +int arr[8];
> +constexpr auto a = W(arr);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
> index 47cdce88e36..3b51bf7c901 100644
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
> @@ -4,13 +4,13 @@
>   
>   constexpr int foo ()
>   try {			// { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } }
> -  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" }
> +  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
>     static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
>     goto l;		// { dg-error "'goto' in 'constexpr' function" }
>     l:;
>     return 0;
>   } catch (...) {
> -  long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" }
> +  long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
>     static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function" }
>     goto l2;		// { dg-error "'goto' in 'constexpr' function" }
>     l2:;
> @@ -19,19 +19,19 @@ try {			// { dg-warning "function-try-block body of 'constexpr' function only av
>   
>   constexpr int bar ()
>   {
> -  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" }
> +  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
>     static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
>     goto l;		// { dg-error "'goto' in 'constexpr' function" }
>     l:;
>     try {			// { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } }
> -    short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" }
> +    short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
>       static float d;	// { dg-error "'d' declared 'static' in 'constexpr' function" }
> -			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target *-*-* } .-1 }
> +			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target c++17_down } .-1 }
>       goto l2;		// { dg-error "'goto' in 'constexpr' function" }
>       l2:;
>       return 0;
>     } catch (int) {
> -    char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" }
> +    char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" "" { target c++17_down } }
>       static int f = 5;	// { dg-error "'f' declared 'static' in 'constexpr' function" }
>       goto l3;		// { dg-error "'goto' in 'constexpr' function" }
>       l3:;
> diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
> index 389b25e16ea..c86aead79af 100644
> --- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
> +++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
> @@ -134,8 +134,8 @@
>   
>   #ifndef __cpp_constexpr
>   #  error "__cpp_constexpr"
> -#elif __cpp_constexpr != 201603
> -#  error "__cpp_constexpr != 201603"
> +#elif __cpp_constexpr != 201907
> +#  error "__cpp_constexpr != 201907"
>   #endif
>   
>   #ifndef __cpp_decltype_auto
> diff --git gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C
> new file mode 100644
> index 00000000000..8ee9b0327a5
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C
> @@ -0,0 +1,15 @@
> +// Test lambda mangling
> +// { dg-do compile { target c++2a } }
> +// { dg-require-weak "" }
> +// { dg-options "-fno-inline" }
> +
> +template<typename T> struct R {
> +  static int x;
> +};
> +// "int i;" makes the op() non-constexpr in C++17.  In C++20, it does not.
> +template<typename T> int R<T>::x = []{int i; return 1;}();
> +template int R<int>::x;
> +// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
> +// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
> +// { dg-final { scan-assembler-not "_ZNK1RIiE1xMUlvE_clEv" } }
> +// { dg-final { scan-assembler-not "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" } }
> diff --git gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
> index d50df25f693..1139f412fda 100644
> --- gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
> +++ gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
> @@ -1,5 +1,5 @@
>   // Origin: PR 44641
> -// { dg-do compile }
> +// { dg-do compile { target c++17_down } }
>   // { dg-options "-gdwarf-2 -O0 -dA" }
>   
>   template <class A> struct MisplacedDbg;
> @@ -40,3 +40,11 @@ struct MisplacedDbg  // { dg-function-on-line {_ZN12MisplacedDbgI3ArgEC[12]Ev} {
>   static MisplacedDbg<Arg> static_var1;
>   static MisplacedDbg<Arg*> static_var2;
>   static MisplacedDbg<Full> static_var3;
> +
> +// This test is skipped in C++20 because we consider the default constructor
> +// MisplacedDbg() constexpr despite the uninitialized member "int i;".  So
> +// the calls to
> +//    MisplacedDbg<Arg>::MisplacedDbg()
> +//    MisplacedDbg<Full>::MisplacedDbg()
> +//    MisplacedDbg<Arg*>::MisplacedDbg()
> +// are elided.  (This comment is here not to mess up the line numbers.)
> diff --git gcc/testsuite/g++.dg/ext/stmtexpr21.C gcc/testsuite/g++.dg/ext/stmtexpr21.C
> index 259cb2f1913..97052a117a7 100644
> --- gcc/testsuite/g++.dg/ext/stmtexpr21.C
> +++ gcc/testsuite/g++.dg/ext/stmtexpr21.C
> @@ -7,7 +7,7 @@ struct test { const int *addr; };
>   const test* setup()
>   {
>     static constexpr test atest =
> -    { ({ int inner; (const int*)(0); }) };  // { dg-error "uninitialized" }
> +    { ({ int inner; (const int*)(0); }) };  // { dg-error "uninitialized" "" { target c++17_down } }
>   
>     return &atest;
>   }
>
diff mbox series

Patch

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index 76d1e4a380e..9766d7f96c4 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -972,7 +972,8 @@  c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_fold_expressions=201603L");
 	  cpp_define (pfile, "__cpp_nontype_template_args=201411L");
 	  cpp_define (pfile, "__cpp_range_based_for=201603L");
-	  cpp_define (pfile, "__cpp_constexpr=201603L");
+	  if (cxx_dialect <= cxx17)
+	    cpp_define (pfile, "__cpp_constexpr=201603L");
 	  cpp_define (pfile, "__cpp_if_constexpr=201606L");
 	  cpp_define (pfile, "__cpp_capture_star_this=201603L");
 	  cpp_define (pfile, "__cpp_inline_variables=201606L");
@@ -991,6 +992,7 @@  c_cpp_builtins (cpp_reader *pfile)
 	{
 	  /* Set feature test macros for C++2a.  */
 	  cpp_define (pfile, "__cpp_conditional_explicit=201806L");
+	  cpp_define (pfile, "__cpp_constexpr=201907L");
 	  cpp_define (pfile, "__cpp_constinit=201907L");
 	  cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806L");
 	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");
diff --git gcc/cp/class.c gcc/cp/class.c
index ef1d5136963..1fd5a7864e5 100644
--- gcc/cp/class.c
+++ gcc/cp/class.c
@@ -5234,8 +5234,14 @@  trivial_default_constructor_is_constexpr (tree t)
   /* A defaulted trivial default constructor is constexpr
      if there is nothing to initialize.  */
   gcc_assert (!TYPE_HAS_COMPLEX_DFLT (t));
-  /* A class with a vptr doesn't have a trivial default ctor.  */
-  return is_really_empty_class (t, /*ignore_vptr*/true);
+  /* A class with a vptr doesn't have a trivial default ctor.
+     In C++20, a class can have transient uninitialized members, e.g.:
+
+       struct S { int i; constexpr S() = default; };
+
+     should work.  */
+  return (cxx_dialect >= cxx2a
+	  || is_really_empty_class (t, /*ignore_vptr*/true));
 }
 
 /* Returns true iff class T has a constexpr default constructor.  */
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 8c79b0484fc..2161aa62d44 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -142,6 +142,7 @@  struct GTY((for_user)) constexpr_fundef {
   tree body;
   tree parms;
   tree result;
+  bool missing_init;
 };
 
 struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
@@ -182,7 +183,7 @@  retrieve_constexpr_fundef (tree fun)
   if (constexpr_fundef_table == NULL)
     return NULL;
 
-  constexpr_fundef fundef = { fun, NULL, NULL, NULL };
+  constexpr_fundef fundef = { fun, NULL, NULL, NULL, false };
   return constexpr_fundef_table->find (&fundef);
 }
 
@@ -762,10 +763,13 @@  massage_constexpr_body (tree fun, tree body)
 }
 
 /* CTYPE is a type constructed from BODY.  Return true if some
-   bases/fields are uninitialized, and complain if COMPLAIN.  */
+   bases/fields are uninitialized, and complain if COMPLAIN.
+   In C++20, we allow missing field initializers.  In such a case
+   record that in MISSING_INIT, if non-null.  */
 
 static bool
-cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
+cx_check_missing_mem_inits (tree ctype, tree body, bool complain,
+			    bool *missing_init = NULL)
 {
   unsigned nelts = 0;
   
@@ -779,7 +783,9 @@  cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
 
   if (TREE_CODE (ctype) == UNION_TYPE)
     {
-      if (nelts == 0 && next_initializable_field (field))
+      if (cxx_dialect < cxx2a
+	  && nelts == 0
+	  && next_initializable_field (field))
 	{
 	  if (complain)
 	    error ("%<constexpr%> constructor for union %qT must "
@@ -815,13 +821,19 @@  cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
 	    continue;
 	  if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
 	    {
-	      /* Recurse to check the anonummous aggregate member.  */
+	      /* Recurse to check the anonymous aggregate member.  */
 	      bad |= cx_check_missing_mem_inits
-		(TREE_TYPE (field), NULL_TREE, complain);
+		(TREE_TYPE (field), NULL_TREE, complain, missing_init);
 	      if (bad && !complain)
 		return true;
 	      continue;
 	    }
+	  if (cxx_dialect >= cxx2a)
+	    {
+	      if (missing_init)
+		*missing_init = true;
+	      continue;
+	    }
 	  ftype = strip_array_types (TREE_TYPE (field));
 	  if (type_has_constexpr_default_constructor (ftype))
 	    {
@@ -847,7 +859,8 @@  cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
 	{
 	  /* Check the anonymous aggregate initializer is valid.  */
 	  bad |= cx_check_missing_mem_inits
-	    (TREE_TYPE (index), CONSTRUCTOR_ELT (body, i)->value, complain);
+	    (TREE_TYPE (index), CONSTRUCTOR_ELT (body, i)->value, complain,
+	     missing_init);
 	  if (bad && !complain)
 	    return true;
 	}
@@ -888,9 +901,11 @@  register_constexpr_fundef (tree fun, tree body)
       return NULL;
     }
 
+  bool missing_init = false;
   if (DECL_CONSTRUCTOR_P (fun)
       && cx_check_missing_mem_inits (DECL_CONTEXT (fun),
-				     massaged, !DECL_GENERATED_P (fun)))
+				     massaged, !DECL_GENERATED_P (fun),
+				     &missing_init))
     return NULL;
 
   /* Create the constexpr function table if necessary.  */
@@ -908,6 +923,7 @@  register_constexpr_fundef (tree fun, tree body)
       DECL_CONTEXT (DECL_RESULT (fun)) = fun;
     }
   entry.body = copy_fn (fun, entry.parms, entry.result);
+  entry.missing_init = missing_init;
   current_function_decl = saved_fn;
   slot = constexpr_fundef_table->find_slot (&entry, INSERT);
   if (clear_ctx)
@@ -2149,9 +2165,24 @@  cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	entry->result = result;
     }
 
-  /* The result of a constexpr function must be completely initialized.  */
+  /* The result of a constexpr function must be completely initialized.
+     Except in C++20 where a constexpr constructor doesn't necessarily
+     have to initialize all the fields, so we set CONSTRUCTOR_NO_CLEARING
+     in order to detect reading an unitialized object in constexpr instead
+     of value-initializing it.  */
   if (TREE_CODE (result) == CONSTRUCTOR)
-    clear_no_implicit_zero (result);
+    {
+      /* But do clear the flag in nested constructors.  */
+      clear_no_implicit_zero (result);
+      if (cxx_dialect >= cxx2a
+	  && DECL_CONSTRUCTOR_P (fun)
+	  && DECL_DECLARED_CONSTEXPR_P (fun))
+	{
+	  constexpr_fundef *f = retrieve_constexpr_fundef (fun);
+	  if (f && f->missing_init)
+	    CONSTRUCTOR_NO_CLEARING (result) = true;
+	}
+    }
 
   pop_cx_call_context ();
   return result;
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 86e38f4af69..ab6da6080a5 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5835,8 +5835,12 @@  check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
      7.1.6 */
   if (VAR_P (decl)
       && !TYPE_REF_P (type)
-      && (constexpr_context_p
-	  || CP_TYPE_CONST_P (type) || var_in_constexpr_fn (decl))
+      && (CP_TYPE_CONST_P (type)
+	  /* C++20 permits trivial default initialization in constexpr
+	     context (P1331R2).  */
+	  || (cxx_dialect < cxx2a
+	      && (constexpr_context_p
+		  || var_in_constexpr_fn (decl))))
       && !DECL_NONTRIVIALLY_INITIALIZED_P (decl))
     {
       tree field = default_init_uninitialized_part (type);
@@ -5845,7 +5849,7 @@  check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
 
       bool show_notes = true;
 
-      if (!constexpr_context_p)
+      if (!constexpr_context_p || cxx_dialect >= cxx2a)
 	{
 	  if (CP_TYPE_CONST_P (type))
 	    {
diff --git gcc/cp/method.c gcc/cp/method.c
index acba6c6da8c..39782970716 100644
--- gcc/cp/method.c
+++ gcc/cp/method.c
@@ -1985,10 +1985,12 @@  walk_field_subobs (tree fields, special_function_kind sfk, tree fnname,
 	  if (bad && deleted_p)
 	    *deleted_p = true;
 
-	  /* For an implicitly-defined default constructor to be constexpr,
-	     every member must have a user-provided default constructor or
-	     an explicit initializer.  */
-	  if (constexpr_p && !CLASS_TYPE_P (mem_type)
+	  /* Before C++20, for an implicitly-defined default constructor to
+	     be constexpr, every member must have a user-provided default
+	     constructor or an explicit initializer.  */
+	  if (constexpr_p
+	      && cxx_dialect < cxx2a
+	      && !CLASS_TYPE_P (mem_type)
 	      && TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE)
 	    {
 	      *constexpr_p = false;
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
index 16eacdde440..48dae5d9609 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C
@@ -4,7 +4,7 @@ 
 struct A
 {
   int i;
-  constexpr A() {}		// { dg-error "A::i" }
+  constexpr A() {}		// { dg-error "A::i" "" { target c++17_down } }
 };
 
 struct B
@@ -12,4 +12,5 @@  struct B
   A a;
 };
 
-constexpr B b[] = { {} };	// { dg-error "A::A" }
+constexpr B b[] = { {} };	// { dg-error "A::A" "" { target c++17_down } }
+// { dg-error "is not a constant expression" "" { target c++2a } .-1 }
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
index 55beda7c49f..1d0fa479cbc 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C
@@ -3,5 +3,5 @@ 
 struct A
 {
   int i;
-  constexpr A() { }		// { dg-error "A::i" }
+  constexpr A() { }		// { dg-error "A::i" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
index e54b26c7f6a..1c43569615c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
@@ -48,7 +48,7 @@  struct Def
 {
   int _M_i;			// { dg-message "does not initialize" }
 
-  constexpr Def() = default;	// { dg-error "implicit declaration is not .constexpr." }
+  constexpr Def() = default;	// { dg-error "implicit declaration is not .constexpr." "" { target c++17_down } }
 };
 
 constexpr Def defobj;		// { dg-error "uninitialized" }
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
index 13ca6fa2390..c603bdd1a00 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C
@@ -21,5 +21,5 @@  struct A1
 struct B1
 {
     A1 a1;
-    constexpr B1() {} // { dg-error "B1::a1" }
+    constexpr B1() {} // { dg-error "B1::a1" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
index a5893563eec..9d6d5ff587c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
@@ -6,7 +6,7 @@ 
 struct A
 {
   int i;
-  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" }
+  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" "" { target c++17_down } }
 };
 
 template <class T>
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
index 12a8d42b31f..71eb559a24c 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
@@ -3,7 +3,7 @@ 
 template <class T> struct A
 {
   T t;
-  constexpr A() { }		// { dg-error "::t" }
+  constexpr A() { }		// { dg-error "::t" "" { target c++17_down } }
 };
 
 int main()
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
index 1a5e832ac34..c22ecc99efb 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C
@@ -14,5 +14,5 @@  union bar
   int x;
   short y;
 
-  constexpr bar() = default;	// { dg-error "constexpr" }
+  constexpr bar() = default;	// { dg-error "constexpr" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
index 15b8b79ecbc..7894ef3051e 100644
--- gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
+++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
@@ -47,17 +47,6 @@  struct S {
 	 []{return 3;}());
 };
 
-template<typename T> struct R {
-  static int x;
-};
-// "int i;" makes the op() non-constexpr in C++17.
-template<typename T> int R<T>::x = []{int i; return 1;}();
-template int R<int>::x;
-// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
-// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
-// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
-// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
-
 void bar()
 {
   // lambdas in non-vague linkage functions have internal linkage.
diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C
new file mode 100644
index 00000000000..9ec13e79bbf
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C
@@ -0,0 +1,15 @@ 
+// Test lambda mangling
+// { dg-do compile { target { c++11 && c++17_down } } }
+// { dg-require-weak "" }
+// { dg-options "-fno-inline" }
+
+template<typename T> struct R {
+  static int x;
+};
+// "int i;" makes the op() non-constexpr in C++17.  In C++20, it does not.
+template<typename T> int R<T>::x = []{int i; return 1;}();
+template int R<int>::x;
+// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
+// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
+// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
+// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
diff --git gcc/testsuite/g++.dg/cpp0x/pr79118.C gcc/testsuite/g++.dg/cpp0x/pr79118.C
index 5db22a96dd4..616b51ea29a 100644
--- gcc/testsuite/g++.dg/cpp0x/pr79118.C
+++ gcc/testsuite/g++.dg/cpp0x/pr79118.C
@@ -13,7 +13,7 @@  struct One
   constexpr One () : a(), b() {} // { dg-error "multiple" }
   constexpr One (int) : a() {}
   constexpr One (unsigned) : b () {}
-  constexpr One (void *) {} // { dg-error "exactly one" }
+  constexpr One (void *) {} // { dg-error "exactly one" "" { target c++17_down } }
 };
 
 One a ();
@@ -30,10 +30,10 @@  struct Two
   };
 
   constexpr Two () : a(), b() {}
-  constexpr Two (int) : a() {} // { dg-error "b' must be initialized" }
-  constexpr Two (unsigned) : b () {} // { dg-error "a' must be initialized" }
-  constexpr Two (void *) {} // { dg-error "a' must be initialized" }
-   // { dg-error "b' must be initialized" "" { target *-*-* } .-1 }
+  constexpr Two (int) : a() {} // { dg-error "b' must be initialized" "" { target c++17_down } }
+  constexpr Two (unsigned) : b () {} // { dg-error "a' must be initialized" "" { target c++17_down } }
+  constexpr Two (void *) {} // { dg-error "a' must be initialized" "" { target c++17_down } }
+   // { dg-error "b' must be initialized" "" { target c++17_down } .-1 }
 };
 
 Two e ();
diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
index 4b1ed5c3a87..2f1218693e0 100644
--- gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C
@@ -2,4 +2,4 @@ 
 // { dg-do compile { target c++14 } }
 
 struct Foo { int m; };
-constexpr void test() { Foo f; }  // { dg-error "uninitialized" }
+constexpr void test() { Foo f; }  // { dg-error "uninitialized" "" { target c++17_down } }
diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
index d82dbada1bf..53f0f1f7a2b 100644
--- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
@@ -8,7 +8,7 @@  constexpr int f(int i) {
   goto foo;			// { dg-error "goto" }
  foo:
   asm("foo");			// { dg-error "asm" "" { target c++17_down } }
-  int k;			// { dg-error "uninitialized" }
+  int k;			// { dg-error "uninitialized" "" { target c++17_down } }
   A a;				// { dg-error "non-literal" }
   return i;
 }
diff --git gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
index a59bf497b30..93b53273741 100644
--- gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
@@ -3,7 +3,7 @@ 
 void f(int i)
 {
   [i]() constexpr {
-    int j;			// { dg-error "uninitialized" }
+    int j;			// { dg-error "uninitialized" "" { target c++17_down } }
     j = i;
     return j;
   }();
diff --git gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
index a18d6aea985..aee1d692a4e 100644
--- gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
+++ gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
@@ -1,5 +1,4 @@ 
-// { dg-do compile { target c++17 } }
-// { dg-options "-I${srcdir}/g++.dg/cpp1y -I${srcdir}/g++.dg/cpp1y/testinc" }
+// { dg-options "-std=c++17 -I${srcdir}/g++.dg/cpp1y -I${srcdir}/g++.dg/cpp1y/testinc" }
 
 //  C++98 features:
 
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
new file mode 100644
index 00000000000..b2248006c03
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
@@ -0,0 +1,100 @@ 
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+// Test basic use.
+
+struct S {
+  int i;
+  constexpr S(bool b) {
+    if (b)
+      i = 42;
+  }
+};
+constexpr S s1(true);
+constexpr S s2(false); // { dg-error "not a constant expression" }
+
+constexpr int
+fn1 (int x)
+{
+  int a;
+  a = 5;
+  return x + a;
+}
+
+static_assert (fn1 (2) == 7);
+
+constexpr int
+fn2 (int x)
+{
+  const int a; // { dg-error "uninitialized .const a." }
+  constexpr int b; // { dg-error "uninitialized .const b." }
+  return x;
+}
+
+constexpr int
+fn3 (int x)
+{
+  int a; // { dg-message ".int a. is not const" }
+  return x + a;
+}
+
+constexpr int a = fn3 (5); // { dg-error "the value of .a. is not usable in a constant expression" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+
+constexpr int
+fn4 ()
+{
+  struct S { int a = -5; int b; } s;
+  return s.a;
+}
+
+static_assert (fn4 () == -5);
+
+constexpr int
+fn5 ()
+{
+  struct S { int a = 9; int b; } s;
+  return s.b;
+}
+
+constexpr int b = fn5 (); // { dg-error "accessing uninitialized member" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+
+constexpr int
+fn6 ()
+{
+  int a;
+  return 42;
+}
+
+static_assert (fn6 () == 42);
+
+constexpr int
+fn7 (bool b)
+{
+  int a; // { dg-message ".int a. is not const" }
+  if (b)
+    a = 42;
+  return a;
+}
+
+static_assert (fn7 (true) == 42);
+static_assert (fn7 (false) == 42); // { dg-error "non-constant condition|the value of .a. is not usable" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+
+constexpr int
+fn8 (int n)
+{
+  int r;
+  switch (n)
+    {
+    case 1:
+    r = n;
+    return r;
+    case 42:
+    r = n;
+    return r;
+    }
+}
+
+static_assert (fn8 (1) == 1);
+static_assert (fn8 (42) == 42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C
new file mode 100644
index 00000000000..0d7b724a040
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C
@@ -0,0 +1,11 @@ 
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+// ??? In c++2a we don't emit a call to _ZN3FooI3ArgEC1Ev.
+
+struct Arg;
+struct Base {
+  int i;
+  virtual ~Base();
+};
+template <class> struct Foo : Base { };
+Foo<Arg> a;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C
new file mode 100644
index 00000000000..541da1c023f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C
@@ -0,0 +1,15 @@ 
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+  int i;
+  constexpr A() : i{} {} 
+};
+
+struct B
+{
+  A a;
+};
+
+constexpr B b[] = { {} };
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C
new file mode 100644
index 00000000000..dd2735289cb
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C
@@ -0,0 +1,16 @@ 
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+  int i;
+  constexpr A() {} 
+};
+
+struct B
+{
+  A a;
+};
+
+// A::i not initialized.
+constexpr B b[] = { {} }; // { dg-error "is not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C
new file mode 100644
index 00000000000..dd614ede2c6
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C
@@ -0,0 +1,61 @@ 
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+// This bullet in [dcl.constexpr] is now gone:
+//  - every non-static data member and base class sub-object shall be initialized
+
+struct A {
+  int i;
+  constexpr A(int _i) { i = _i; }
+};
+
+struct B {
+  int i;
+  constexpr B() { }
+};
+
+// Anonymous members.
+struct E {
+  int a;
+  union {
+    char b;
+    __extension__ struct {
+      double c;
+      long d;
+    };  
+    union {
+      char e;
+      void *f; 
+    };  
+  };  
+  __extension__ struct {
+    long long g;
+    __extension__ struct {
+      int h;
+      double i;
+    };  
+    union {
+      char *j; 
+      E *k; 
+    };  
+  };  
+
+  // Completely initialized.
+  constexpr E(int(&)[1]) : a(), b(), g(), h(), i(), j() {}
+  constexpr E(int(&)[3]) : a(), e(), g(), h(), i(), k() {}
+  constexpr E(int(&)[7]) : a(), b(), g(), h(), i(), j() {}
+  constexpr E(int(&)[8]) : a(), f(), g(), h(), i(), k() {}
+  constexpr E(int(&)[9]) : a(), c(), d(), g(), h(), i(), k() {}
+
+  // Missing d, i, j/k union init.
+  constexpr E(int(&)[2]) : a(), c(), g(), h() {}
+
+  // Missing h, j/k union init.
+  constexpr E(int(&)[4]) : a(), c(), d(), g(), i() {}
+
+  // Missing b/c/d/e/f union init.
+  constexpr E(int(&)[5]) : a(), g(), h(), i(), k() {}
+
+  // Missing a, b/c/d/e/f union, g/h/i/j/k struct init.
+  constexpr E(int(&)[6]) {}
+};
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C
new file mode 100644
index 00000000000..5c2b545086c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C
@@ -0,0 +1,23 @@ 
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S { int i; };
+
+constexpr void
+fn ()
+{
+  S s;
+
+  []() constexpr {
+    int i;
+  }();
+}
+
+constexpr int
+fn2 ()
+{
+  return __extension__ ({ int n; n; });
+}
+
+constexpr int i = fn2 (); // { dg-error "not usable in a constant expression" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C
new file mode 100644
index 00000000000..a2994f5272c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C
@@ -0,0 +1,26 @@ 
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+/* We used to get the "constexpr constructor for union S::<unnamed union>
+   must initialize exactly one non-static data member" error, but not anymore
+   in C++20.  */
+
+struct S {
+  union {
+    int i;
+    double d;
+  };
+  constexpr S() { }
+};
+
+union U {
+  int a;
+  constexpr U() { }
+};
+
+struct W {
+  union {
+    int a;
+  };
+  constexpr W() { }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C
new file mode 100644
index 00000000000..dd2741efa8c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C
@@ -0,0 +1,63 @@ 
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  int a = 1;
+  constexpr S() = default;
+};
+
+constexpr S s;
+
+union U {
+  int a = 1;
+  constexpr U() = default;
+};
+
+constexpr U u;
+
+struct S2 {
+  int a;
+  constexpr S2() = default;
+};
+
+constexpr S2 s2; // { dg-error "uninitialized .const s2." }
+
+union U2 {
+  int a;
+  constexpr U2() = default;
+};
+
+constexpr U2 u2; // { dg-error "uninitialized .const u2." }
+
+struct S3 {
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int a;
+  } u;
+  constexpr S3() = default;
+};
+
+constexpr S3 s3; // { dg-error "uninitialized .const s3." }
+
+struct S4 {
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int n;
+  } u;
+  constexpr S4() = default;
+};
+
+constexpr S4 s4; // { dg-error "uninitialized .const s4." }
+
+struct S5 {
+  union {
+    int n = 0;
+  };
+  // FIXME if it's anonymous union, we don't give the error below
+  union {
+    int m;
+  } u;
+  constexpr S5() = default;
+};
+
+constexpr S5 s5; // { dg-error "uninitialized .const s5.|not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C
new file mode 100644
index 00000000000..0d5a4a79c90
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C
@@ -0,0 +1,15 @@ 
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+constexpr auto a = W(42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C
new file mode 100644
index 00000000000..b44098cc89b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C
@@ -0,0 +1,17 @@ 
+// PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts.
+// { dg-do compile { target c++2a } }
+
+struct S {
+  constexpr S(int) {}
+};
+
+struct W {
+  constexpr W(int (&)[8]) : W(8) { }
+  constexpr W(int) : s(8), p() {}
+
+  S s;
+  int *p;
+};
+
+int arr[8];
+constexpr auto a = W(arr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
index 47cdce88e36..3b51bf7c901 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
@@ -4,13 +4,13 @@ 
 
 constexpr int foo ()
 try {			// { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } }
-  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" }
+  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
   static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
   goto l;		// { dg-error "'goto' in 'constexpr' function" }
   l:;
   return 0;
 } catch (...) {
-  long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" }
+  long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
   static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function" }
   goto l2;		// { dg-error "'goto' in 'constexpr' function" }
   l2:;
@@ -19,19 +19,19 @@  try {			// { dg-warning "function-try-block body of 'constexpr' function only av
 
 constexpr int bar ()
 {
-  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" }
+  int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
   static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
   goto l;		// { dg-error "'goto' in 'constexpr' function" }
   l:;
   try {			// { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } }
-    short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" }
+    short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
     static float d;	// { dg-error "'d' declared 'static' in 'constexpr' function" }
-			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target *-*-* } .-1 }
+			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target c++17_down } .-1 }
     goto l2;		// { dg-error "'goto' in 'constexpr' function" }
     l2:;
     return 0;
   } catch (int) {
-    char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" }
+    char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" "" { target c++17_down } }
     static int f = 5;	// { dg-error "'f' declared 'static' in 'constexpr' function" }
     goto l3;		// { dg-error "'goto' in 'constexpr' function" }
     l3:;
diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 95251c2f5c6..7b96b76a3f3 100644
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -134,8 +134,8 @@ 
 
 #ifndef __cpp_constexpr
 #  error "__cpp_constexpr"
-#elif __cpp_constexpr != 201603
-#  error "__cpp_constexpr != 201603"
+#elif __cpp_constexpr != 201907
+#  error "__cpp_constexpr != 201907"
 #endif
 
 #ifndef __cpp_decltype_auto
diff --git gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C
new file mode 100644
index 00000000000..8ee9b0327a5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C
@@ -0,0 +1,15 @@ 
+// Test lambda mangling
+// { dg-do compile { target c++2a } }
+// { dg-require-weak "" }
+// { dg-options "-fno-inline" }
+
+template<typename T> struct R {
+  static int x;
+};
+// "int i;" makes the op() non-constexpr in C++17.  In C++20, it does not.
+template<typename T> int R<T>::x = []{int i; return 1;}();
+template int R<int>::x;
+// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
+// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
+// { dg-final { scan-assembler-not "_ZNK1RIiE1xMUlvE_clEv" } }
+// { dg-final { scan-assembler-not "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" } }
diff --git gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
index d50df25f693..1139f412fda 100644
--- gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
+++ gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C
@@ -1,5 +1,5 @@ 
 // Origin: PR 44641
-// { dg-do compile }
+// { dg-do compile { target c++17_down } }
 // { dg-options "-gdwarf-2 -O0 -dA" }
 
 template <class A> struct MisplacedDbg;
@@ -40,3 +40,11 @@  struct MisplacedDbg  // { dg-function-on-line {_ZN12MisplacedDbgI3ArgEC[12]Ev} {
 static MisplacedDbg<Arg> static_var1;
 static MisplacedDbg<Arg*> static_var2;
 static MisplacedDbg<Full> static_var3;
+
+// This test is skipped in C++20 because we consider the default constructor
+// MisplacedDbg() constexpr despite the uninitialized member "int i;".  So
+// the calls to
+//    MisplacedDbg<Arg>::MisplacedDbg()
+//    MisplacedDbg<Full>::MisplacedDbg()
+//    MisplacedDbg<Arg*>::MisplacedDbg()
+// are elided.  (This comment is here not to mess up the line numbers.)
diff --git gcc/testsuite/g++.dg/ext/stmtexpr21.C gcc/testsuite/g++.dg/ext/stmtexpr21.C
index 259cb2f1913..97052a117a7 100644
--- gcc/testsuite/g++.dg/ext/stmtexpr21.C
+++ gcc/testsuite/g++.dg/ext/stmtexpr21.C
@@ -7,7 +7,7 @@  struct test { const int *addr; };
 const test* setup()
 {
   static constexpr test atest =
-    { ({ int inner; (const int*)(0); }) };  // { dg-error "uninitialized" }
+    { ({ int inner; (const int*)(0); }) };  // { dg-error "uninitialized" "" { target c++17_down } }
 
   return &atest;
 }