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