Bug 54835 - [C++11][DR 1518] Explicit default constructors not respected during copy-list-initialization
Summary: [C++11][DR 1518] Explicit default constructors not respected during copy-list...
Status: ASSIGNED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.8.0
: P3 normal
Target Milestone: ---
Assignee: Jason Merrill
URL:
Keywords: accepts-invalid
: 58399 66537 (view as bug list)
Depends on:
Blocks: 60417
  Show dependency treegraph
 
Reported: 2012-10-06 11:20 UTC by Daniel Krügler
Modified: 2021-06-09 10:20 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2013-01-24 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Daniel Krügler 2012-10-06 11:20:07 UTC
gcc 4.8.0 20120930 (experimental) with the compiler flags

-Wall -pedantic -std=c++11 

accepts the following copy-list-initialization of a class type with an explicit default constructor:

//-------------------------
struct S {
  explicit S(int = 0) {}
};

S s = {};
//-------------------------

This code should be ill-formed, because it is ruled out by [over.match.list], in particular by the unconditional wording:

"In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed."
Comment 1 Jonathan Wakely 2013-01-24 13:32:34 UTC
confirmed, not a regression
Comment 2 Jason Merrill 2013-02-15 20:56:04 UTC
It's not clear to me that [over.match.list] applies to this initialization, since the value-initialization bullet is separate from the bullet that cross-references [over.match.list].  And in fact there's code in convert_like_real specifically to avoid this diagnostic because of this difference, and it's tested for in initlist40.C.

I'm not opposed to this behavior, but I think it would be a language change.
Comment 3 Daniel Krügler 2013-02-16 11:57:21 UTC
(In reply to comment #2)
> I'm not opposed to this behavior, but I think it would be a language change.

Thanks Jason. I just see now

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1518

Unless I'm mistaken, this is actually the relevant issue. I suggest to defer this issue and mark it with CWG 1518. I'm not sure how this would be best done, so leave it to the administrators of this list.
Comment 4 Jason Merrill 2013-02-16 15:02:22 UTC
Ah, good point.  I think we decided in Portland to go with the behavior you expect; all that's left is the drafting (which is also for me to do).  Thanks.
Comment 5 Jason Merrill 2013-03-17 02:36:25 UTC
Author: jason
Date: Sun Mar 17 02:36:08 2013
New Revision: 196732

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=196732
Log:
	DR 1518
	PR c++/54835
	* call.c (convert_like_real): Check for explicit constructors
	even for value-initialization.

Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/call.c
    trunk/gcc/testsuite/g++.dg/cpp0x/initlist40.C
Comment 6 Jason Merrill 2013-03-23 05:11:37 UTC
Ah, good point.  I think we decided in Portland to go with the behavior you expect; all that's left is the drafting (which is also for me to do).  Thanks.

Fixed for 4.9.
Comment 7 Jason Merrill 2013-03-23 05:12:08 UTC
x
Comment 8 Jonathan Wakely 2013-09-12 08:44:12 UTC
*** Bug 58399 has been marked as a duplicate of this bug. ***
Comment 9 Jason Merrill 2015-05-04 15:29:37 UTC
I believe that the resolution of DR 1630 clarifies that value-initialization can invoke an explicit constructor even in copy-list-initialization context, so I should revert my change for this PR.
Comment 10 Daniel Krügler 2015-05-04 18:35:55 UTC
(In reply to Jason Merrill from comment #9)
> I believe that the resolution of DR 1630 clarifies that value-initialization
> can invoke an explicit constructor even in copy-list-initialization context,
> so I should revert my change for this PR.

I read DR 1630 again and cannot follow that conclusion - could you clarify? It still says "For copy-initialization, the candidate functions are all the converting constructors (12.3.1 [class.conv.ctor]) of that class" and the issue example uses an explicit default constructor.
Comment 11 Jason Merrill 2015-05-06 02:08:06 UTC
Author: jason
Date: Wed May  6 02:07:34 2015
New Revision: 222836

URL: https://gcc.gnu.org/viewcvs?rev=222836&root=gcc&view=rev
Log:
	DR 1518
	DR 1630
	PR c++/54835
	PR c++/60417
	* call.c (convert_like_real): Check value-initialization before
	explicit.
	* typeck2.c (process_init_constructor_record): Don't set
	CONSTRUCTOR_IS_DIRECT_INIT.
	(process_init_constructor_array): Likewise.
	* init.c (build_vec_init): Likewise.

Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/initlist-dr1518.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/call.c
    trunk/gcc/cp/init.c
    trunk/gcc/cp/typeck2.c
    trunk/gcc/testsuite/g++.dg/cpp0x/initlist40.C
Comment 12 Jason Merrill 2015-05-06 18:09:03 UTC
(In reply to Daniel Krügler from comment #10)
> I read DR 1630 again and cannot follow that conclusion - could you clarify?
> It still says "For copy-initialization, the candidate functions are all the
> converting constructors (12.3.1 [class.conv.ctor]) of that class" and the
> issue example uses an explicit default constructor.

Yes, but the previous sentence now says "For direct-initialization or default-initialization, the candidate functions are all the constructors of the class of the object being initialized."  This is default-initialization by way of value-initialization, so I think this sentence takes priority over the one you quote.
Comment 13 Jason Merrill 2015-06-15 16:03:13 UTC
*** Bug 66537 has been marked as a duplicate of this bug. ***
Comment 14 Jason Merrill 2015-06-15 16:03:38 UTC
Suspended pending resolution of DR 1518.
Comment 15 Jason Merrill 2015-10-24 02:58:44 UTC
Author: jason
Date: Sat Oct 24 02:58:10 2015
New Revision: 229283

URL: https://gcc.gnu.org/viewcvs?rev=229283&root=gcc&view=rev
Log:
	DR 1518
	DR 1630
	PR c++/54835
	PR c++/60417
	* call.c (convert_like_real): Value-initialization can't use
	explicit constructors in C++11 and up.

Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/explicit10.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/call.c
    trunk/gcc/testsuite/g++.dg/cpp0x/initlist40.C
    trunk/gcc/testsuite/g++.dg/init/explicit1.C
    trunk/gcc/testsuite/g++.dg/init/explicit2.C
Comment 16 Jason Merrill 2015-10-24 03:00:03 UTC
Since the last meeting it has been suggested that this effect of DR 1630 was unintended, and the testcase should be ill-formed.  So I've changed it back.
Comment 17 Jonathan Wakely 2018-04-30 13:24:48 UTC
Jason, should this be FIXED instead of SUSPENDED?
Comment 18 Martin Liška 2018-11-19 12:57:24 UTC
Can the bug be marked as resolved?
Comment 19 David Friberg 2021-05-11 10:38:13 UTC
Note that this bug report was marked as suspended based on the resolution of CWG 1518 (and the related DR 1630) in June 2015, but the resolution of both these issue were since changed. Citing 1518:

> Additional note, October, 2015:
> 
> It has been suggested that **the resolution of issue 1630 went too**
> far in allowing use of explicit constructors for default initialization, 
> and that default initialization should be considered to model copy 
> initialization instead. The resolution of this issue would provide an
> opportunity to adjust that.

P0398R0 [1] describes the final resolution to CWG 1518, after which the following example is arguably well-formed:

 struct tag_t {
     explicit constexpr tag_t() = default;  // #3
 };

 struct S {
     constexpr S() {}
     constexpr S(S const&) {}
     S& operator=(S const&) { return *this; }
     S& operator=(tag_t) { return *this; }     // #1
 };

 int main() {
     S s{};
     s = {};  // #2: GCC error: ambiguous overload for 'operator='
 }


as (at least from C++17) #1 is not a viable overload for the assignment at #2, as tag_t is not an aggregate and copy-list-init from empty braces will not consider the ctor #3 as it is explicit.

The example above is rejected by GCC (various versions) for both C++17 and C++20, whereas Clang and MSVC both accepts it (curiosly Clang accepts it also for C++11 for C++14, which may be wrong as tag_t is an aggregate for these case, but I'm unsure, as we are covering a lot of CWG/LWG/DR confusion for this issue).

---

[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0398r0.html
Comment 20 Jonathan Wakely 2021-05-11 11:25:47 UTC
Un-suspending based on the previous comment.
Comment 21 TC 2021-06-07 05:30:11 UTC
(In reply to David Friberg from comment #19)
> 
> P0398R0 [1] describes the final resolution to CWG 1518, after which the
> following example is arguably well-formed:
> 

It's not. Explicitness of a constructor is not considered when forming implicit conversion sequences from a braced-init-list, and therefore the assignment is ambiguous because {} can convert to either S or tag_t, even though the latter is ill-formed if actually used.
Comment 22 David Friberg 2021-06-09 10:20:32 UTC
(In reply to TC from comment #21)
> (In reply to David Friberg from comment #19)
> > 
> > P0398R0 [1] describes the final resolution to CWG 1518, after which the
> > following example is arguably well-formed:
> > 
> 
> It's not. Explicitness of a constructor is not considered when forming
> implicit conversion sequences from a braced-init-list, and therefore the
> assignment is ambiguous because {} can convert to either S or tag_t, even
> though the latter is ill-formed if actually used.

TC, thanks for the clarification. Just to be sure I'm not missing something, I was under the impression that the [**emphasis**]

> For direct-initialization or default-initialization
> **that is not in the context of copy-initialization**, 
> the candidate functions are all the constructors of 
> the class of the object being initialized.

part of [over.match.ctor]/1 [1], which was added in P0398R0, intended to remove non-converting (explicit) constructors from overload resolution in this context, particularly resolving the issue shown in the original example of LWG issue 251 [2]:


> That turns out to be very unfortunate, consider the following:
>
> #include <memory>
> #include <array>
>
> void f(std::array<int, 1>, int) {} // #1
> void f(std::allocator_arg_t, int) {} // #2
>
> int main()
> {
>   f({}, 666); // #3
> }
> 
> The call at #3 is ambiguous.

after which the call at #3 in the LWG example above is no longer ambiguous (afaict). I may be missing some subtlety here, but does my example above not fall into the same category as this one?

[1] https://timsong-cpp.github.io/cppwp/n4861/over.match.ctor#1
[2] https://cplusplus.github.io/LWG/issue2510