Bug 63707 - Brace initialization of array sometimes fails if no copy constructor
Summary: Brace initialization of array sometimes fails if no copy constructor
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.9.1
: P3 normal
Target Milestone: 9.4
Assignee: Jason Merrill
URL:
Keywords: rejects-valid
: 64887 70395 92393 (view as bug list)
Depends on:
Blocks:
 
Reported: 2014-11-01 18:44 UTC by John Lindgren
Modified: 2021-01-29 19:31 UTC (History)
8 users (show)

See Also:
Host:
Target:
Build:
Known to work: 11.0
Known to fail: 10.2.0, 4.8.2, 4.9.1, 5.0, 6.1.1
Last reconfirmed: 2020-12-07 00:00:00


Attachments
Test case 1 (127 bytes, text/x-c)
2014-11-01 18:44 UTC, John Lindgren
Details
Test case 2 (127 bytes, text/x-c)
2014-11-01 18:44 UTC, John Lindgren
Details
Test case 3 (99 bytes, text/x-c)
2014-11-01 18:45 UTC, John Lindgren
Details
Test case 4 (115 bytes, text/x-c)
2014-11-01 18:45 UTC, John Lindgren
Details
Corrected test case 2 (123 bytes, text/x-c)
2014-11-01 18:49 UTC, John Lindgren
Details

Note You need to log in before you can comment on or make changes to this bug.
Description John Lindgren 2014-11-01 18:44:27 UTC
Created attachment 33859 [details]
Test case 1

Brace-initializing an array of a class without a public copy constructor gives an error in some cases but succeeds in others.  After discussing the problem here [1], I believe this is bug in GCC.

Attached are three test cases:
test1-fails.cc: Initialization of member array of non-literal type (error)
test2-works.cc: Initialization of member array of literal type (compiles)
test3-works.cc: Initialization of static array of non-literal type (compiles)
test4-works.cc: Initialization of single member of non-literal type (compiles)

The error given is:
$ g++ -Wall -Wextra -std=c++11 -c test1-fails.cc 
test1-fails.cc: In constructor ‘Parent::Parent()’:
test1-fails.cc:13:35: error: use of deleted function ‘Child::Child(const Child&)’
     Parent () : children {{5}, {7}} {}
                                   ^
test1-fails.cc:7:5: note: declared here
     Child (const Child &) = delete;
     ^

$ g++ --version
g++ (GCC) 4.9.1 20140903 (prerelease)

By way of comparison, Clang 3.5.0 compiles all three test cases without errors.

[1] http://stackoverflow.com/questions/26685551/how-to-initialize-array-of-classes-with-deleted-copy-constructor-c11
Comment 1 John Lindgren 2014-11-01 18:44:51 UTC
Created attachment 33860 [details]
Test case 2
Comment 2 John Lindgren 2014-11-01 18:45:31 UTC
Created attachment 33861 [details]
Test case 3
Comment 3 John Lindgren 2014-11-01 18:45:46 UTC
Created attachment 33862 [details]
Test case 4
Comment 4 John Lindgren 2014-11-01 18:49:23 UTC
Created attachment 33863 [details]
Corrected test case 2

Corrected test case 2 (previous was identical to test case 1).
Comment 5 Jonathan Wakely 2015-01-28 11:52:23 UTC
The member array case only fails when the array element has a user-defined destructor

Here's the failing testcase repeated from the attachment and reduced slightly:

struct Child
{
    Child (int);
    ~Child ();
    Child (const Child &) = delete;
};

struct Parent
{
    Parent () : children {{5}, {7}} {}

    Child children[2];
};


i.cc: In constructor ‘Parent::Parent()’:
i.cc:12:35: error: use of deleted function ‘Child::Child(const Child&)’
     Parent () : children {{5}, {7}} {}
                                   ^
i.cc:6:5: note: declared here
     Child (const Child &) = delete;
     ^

Commenting out the destructor declaration allows it to compile.
Comment 6 Jonathan Wakely 2015-01-31 20:34:03 UTC
*** Bug 64887 has been marked as a duplicate of this bug. ***
Comment 7 Jonathan Wakely 2015-01-31 20:35:24 UTC
Reduced from PR 64887:

struct string
{
  string(const char*) { }
  ~string(); // make this type non-trivial
};

struct A
{
  string s;
  A() = delete;
  A(const A&) = delete;
  A(A&&) = delete;
  A(const char*);
};

A arr[2] = {{"a"}, {"b"}}; // ok

struct Aggr {
  A arr[2];
  Aggr() : arr{{"a"}, {"b"}} {} // error
};

The member of non-trivial type is required to trigger the error, presumably because it makes the destructor non-trivial.
Comment 8 John Lindgren 2016-06-07 13:10:47 UTC
Just ran into this bug again with GCC 6.1.1.  Any hope of this ever getting fixed?
Comment 9 Jonathan Wakely 2018-04-30 13:21:14 UTC
*** Bug 70395 has been marked as a duplicate of this bug. ***
Comment 10 Jonathan Wakely 2018-04-30 13:21:55 UTC
Testcase from PR 70395

struct NonCopyable {
  NonCopyable(const NonCopyable&) = delete;
  NonCopyable(NonCopyable&&) = delete;
  NonCopyable& operator=(const NonCopyable&) = delete;
  NonCopyable& operator=(NonCopyable&&) = delete;

  NonCopyable() {}

  ~NonCopyable() {} // to make it non-trivial
};

struct A {
  A(): _a{} {}
  ~A() {}

  NonCopyable _a[5];
} a;
Comment 11 Иван Бубников 2019-11-20 06:49:55 UTC
*** Bug 92393 has been marked as a duplicate of this bug. ***
Comment 12 Eyal Rozenberg 2020-12-06 19:59:49 UTC
Rejection valid code, especially valid code which is not contrived and can well occur in people's real-life work, seems like a high-priority bug.

The last substantive comment here, other than dupe-marking-related comments two years ago, is comment #8, asking for this to be fixed - four and a half years ago.

Jonathan and others - please try to prioritize fixing this, and even if you can't for some reason - at least explain which this can't be fixed promptly.

See also:

https://stackoverflow.com/q/65138048/1593077
Comment 13 Jakub Jelinek 2020-12-10 19:17:22 UTC
At least for #c5 the important difference between that testcase and // ~Child ();
which is accepted is I think since r0-113052-ge2df21bfc6c81b4bc410a42002c8427454ffa628
in the cp/init.c (perform_member_init) code:
888	      /* A FIELD_DECL doesn't really have a suitable lifetime, but
889		 make_temporary_var_for_ref_to_temp will treat it as automatic and
890		 set_up_extended_ref_temp wants to use the decl in a warning.  */
891	      init = extend_ref_init_temps (member, init, &cleanups);
892	      if (TREE_CODE (type) == ARRAY_TYPE
893		  && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (type)))
894		init = build_vec_init_expr (type, init, tf_warning_or_error);
If it has trivial destructor, nothing calls build_vec_init_expr and the copy ctor isn't needed, but when it is called, it is needed.
I think in this case extend_ref_init_temps doesn't do anything at all, so it is unclear if build_vec_init_expr is needed too or not.
Comment 14 Peter Dimov 2021-01-12 23:35:00 UTC
FWIW, I just hit this problem as well when trying to make changes to Boost.Variant2. My reduced test case is https://godbolt.org/z/zG6ddP; I was going to submit that as a bug but found this one.

I support the petition to have this fixed.
Comment 15 Marek Polacek 2021-01-13 01:32:28 UTC
Looking.
Comment 16 Jason Merrill 2021-01-14 20:36:53 UTC
Mine.
Comment 17 CVS Commits 2021-01-15 18:39:03 UTC
The master branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:cd09079cfd50d289cbb05eadb728a0713f6bae8a

commit r11-6733-gcd09079cfd50d289cbb05eadb728a0713f6bae8a
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Jan 15 11:42:00 2021 -0500

    c++: Fix list-init of array of no-copy type [PR63707]
    
    build_vec_init_elt models initialization from some arbitrary object of the
    type, i.e. copy, but in the case of list-initialization we don't do a copy
    from the elements, we initialize them directly.
    
    gcc/cp/ChangeLog:
    
            PR c++/63707
            * tree.c (build_vec_init_expr): Don't call build_vec_init_elt
            if we got a CONSTRUCTOR.
    
    gcc/testsuite/ChangeLog:
    
            PR c++/63707
            * g++.dg/cpp0x/initlist-array13.C: New test.
Comment 18 Jason Merrill 2021-01-15 18:57:56 UTC
Fixed for GCC 11 so far.
Comment 19 CVS Commits 2021-01-29 16:00:34 UTC
The releases/gcc-10 branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:81fd2df5911c4b2c31960fa5ef1407d20f1ca50c

commit r10-9311-g81fd2df5911c4b2c31960fa5ef1407d20f1ca50c
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Jan 15 11:42:00 2021 -0500

    c++: Fix list-init of array of no-copy type [PR63707]
    
    build_vec_init_elt models initialization from some arbitrary object of the
    type, i.e. copy, but in the case of list-initialization we don't do a copy
    from the elements, we initialize them directly.
    
    gcc/cp/ChangeLog:
    
            PR c++/63707
            * tree.c (build_vec_init_expr): Don't call build_vec_init_elt
            if we got a CONSTRUCTOR.
    
    gcc/testsuite/ChangeLog:
    
            PR c++/63707
            * g++.dg/cpp0x/initlist-array13.C: New test.
Comment 20 CVS Commits 2021-01-29 16:23:28 UTC
The releases/gcc-9 branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:ca02f0a0eb0fbeb3c1e01c3a55a95b0690ecfabc

commit r9-9212-gca02f0a0eb0fbeb3c1e01c3a55a95b0690ecfabc
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Jan 15 11:42:00 2021 -0500

    c++: Fix list-init of array of no-copy type [PR63707]
    
    build_vec_init_elt models initialization from some arbitrary object of the
    type, i.e. copy, but in the case of list-initialization we don't do a copy
    from the elements, we initialize them directly.
    
    gcc/cp/ChangeLog:
    
            PR c++/63707
            * tree.c (build_vec_init_expr): Don't call build_vec_init_elt
            if we got a CONSTRUCTOR.
    
    gcc/testsuite/ChangeLog:
    
            PR c++/63707
            * g++.dg/cpp0x/initlist-array13.C: New test.
Comment 21 Jason Merrill 2021-01-29 18:18:10 UTC
Also fixed for 9.4 and 10.3.
Comment 22 Eyal Rozenberg 2021-01-29 19:31:21 UTC
Thank you, Jason and others, for your attentiveness to interest in the bug and prioritizing its fix. (Now if you could just fix all the other bugs I'm interested in... :-P)