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