On 2/13/20 3:59 PM, Jason Merrill wrote:
On 2/12/20 9:21 PM, Martin Sebor wrote:
On 2/11/20 5:28 PM, Jason Merrill wrote:
On 2/11/20 9:00 PM, Martin Sebor wrote:
r270155, committed in GCC 9, introduced a transformation that strips
redundant trailing zero initializers from array initializer lists in
order to support string literals as template arguments.

The transformation neglected to consider the case of array elements
of trivial class types with user-defined conversion ctors and either
defaulted or deleted default ctors.  (It didn't occur to me that
those qualify as trivial types despite the user-defined ctors.)  As
a result, some valid initialization expressions are rejected when
the explicit zero-initializers are dropped in favor of the (deleted)
default ctor,

Hmm, a type with only a deleted default constructor is not trivial, that should have been OK already.

For Marek's test case:
   struct A { A () == delete; A (int) = delete; };

trivial_type_p() returns true (as does __is_trivial (A) in both GCC
and Clang).

[class.prop] says that

   A trivial class is a class that is trivially copyable and has one
   or more default constructors (10.3.4.1), all of which are either
   trivial or deleted and at least one of which is not deleted.

That sounds like A above is not trivial because it doesn't have
at least one default ctor that's not deleted, but both GCC and
Clang say it is.  What am I missing?  Is there some other default
constructor hiding in there that I don't know about?

and others are eliminated in favor of the defaulted
ctor instead of invoking a user-defined conversion ctor, leading to
wrong code.

This seems like a bug in type_initializer_zero_p; it shouldn't treat 0 as a zero initializer for any class.

That does fix it, and it seems like the right solution to me as well.
Thanks for the suggestion.  I'm a little unsure about the condition
I put in place though.

Attached is an updated patch rested on x86_64-linux.

-  if (sized_array_p && trivial_type_p (elt_type))
+  if (sized_array_p
+      && trivial_type_p (elt_type)
+      && !TYPE_NEEDS_CONSTRUCTING (elt_type))

Do we still need this change?  If so, please add a comment about the trivial_type_p bug.

The change isn't needed with my patch as it was, but it would still
be needed with the changes you suggested (even then it doesn't help
with the problem I describe below).


   if (TREE_CODE (init) != CONSTRUCTOR
I might change this to

  if (!CP_AGGREGATE_TYPE_P (type))
    return initializer_zerop (init);

This behaves differently in C++ 2a mode (the whole condition evaluates
to true for class A below) than in earlier modes and causes a failure
in the new array55.C test:

  struct A
  {
    A () = delete;
    A (int) = delete;
  };

A a1[1] = { 0 }; // { dg-error "use of deleted function 'A::A\\\(int\\\)'" }

because GCC ends up printing:

  error: use of deleted function 'A::A()'

This is because A is considered trivial and TYPE_NEEDS_CONSTRUCTING
is false.  IIUC what Marek pointed to (CWG1496), A should not be
considered trivial but GCC doesn't implement that change in
the standard, hence the failure.

  else if (TREE_CODE (init) != CONSTRUCTOR)
    return false;

and then remove the

  if (TYPE_NON_AGGREGATE_CLASS (type))
    return false;

later in the function.

More generally, this function could recognize when the initializer is equivalent to {}-initialization and return true in that case, but that sounds probably too tricky for stage 4.

My preference is to leave the fix as it is (i.e., leave the test
for RECORD_OR_UNION_TYPE_P in place), just with
the TYPE_NEEDS_CONSTRUCTING test removed, and defer improvements
until PR 85723 is fixed.  Let me know how you want me to proceed.

Martin

Reply via email to