https://gcc.gnu.org/g:d435571b54b02946c97b5b24f20e5a7058fd96a1
commit r14-9946-gd435571b54b02946c97b5b24f20e5a7058fd96a1 Author: Jason Merrill <ja...@redhat.com> Date: Fri Apr 12 13:24:44 2024 -0400 c++: reference list-init, conversion fn [PR113141] The original testcase in PR113141 is an instance of CWG1996; the standard fails to consider conversion functions when initializing a reference directly from an initializer-list of one element, but then does consider them when initializing a temporary. I have a proposed fix for this defect, which is implemented here. DR 1996 PR c++/113141 gcc/cp/ChangeLog: * call.cc (reference_binding): Check direct binding from a single-element list. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/initlist-ref1.C: New test. * g++.dg/cpp0x/initlist-ref2.C: New test. * g++.dg/cpp0x/initlist-ref3.C: New test. Co-authored-by: Patrick Palka <ppa...@redhat.com> Diff: --- gcc/cp/call.cc | 21 +++++++++++++++++---- gcc/testsuite/g++.dg/cpp0x/initlist-ref1.C | 16 ++++++++++++++++ gcc/testsuite/g++.dg/cpp0x/initlist-ref2.C | 10 ++++++++++ gcc/testsuite/g++.dg/cpp0x/initlist-ref3.C | 13 +++++++++++++ 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 9568b5eb2c4..15b5647298e 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -1596,7 +1596,9 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p, return conv; } -/* Returns nonzero if T1 is reference-related to T2. */ +/* Returns nonzero if T1 is reference-related to T2. + + This is considered when a reference to T1 is initialized by a T2. */ bool reference_related_p (tree t1, tree t2) @@ -1757,6 +1759,7 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags, } bool copy_list_init = false; + bool single_list_conv = false; if (expr && BRACE_ENCLOSED_INITIALIZER_P (expr)) { maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); @@ -1783,6 +1786,11 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags, from = etype; goto skip; } + else if (CLASS_TYPE_P (etype) && TYPE_HAS_CONVERSION (etype)) + /* CWG1996: jason's proposed drafting adds "or initializing T from E + would bind directly". We check that in the direct binding with + conversion code below. */ + single_list_conv = true; } /* Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is copy-list-initialized, and the reference is bound @@ -1907,9 +1915,14 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags, (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it).... */ - else if (CLASS_TYPE_P (from) && !related_p - && !(flags & LOOKUP_NO_CONVERSION)) + else if (!related_p + && !(flags & LOOKUP_NO_CONVERSION) + && (CLASS_TYPE_P (from) || single_list_conv)) { + tree rexpr = expr; + if (single_list_conv) + rexpr = CONSTRUCTOR_ELT (expr, 0)->value; + /* [dcl.init.ref] If the initializer expression @@ -1923,7 +1936,7 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags, the reference is bound to the lvalue result of the conversion in the second case. */ - z_candidate *cand = build_user_type_conversion_1 (rto, expr, flags, + z_candidate *cand = build_user_type_conversion_1 (rto, rexpr, flags, complain); if (cand) { diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-ref1.C b/gcc/testsuite/g++.dg/cpp0x/initlist-ref1.C new file mode 100644 index 00000000000..f893f12dafa --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/initlist-ref1.C @@ -0,0 +1,16 @@ +// PR c++/113141 +// { dg-do compile { target c++11 } } + +struct ConvToRef { + operator int&(); +}; + +struct A { int& r; }; + +void f(A); + +int main() { + ConvToRef c; + A a{{c}}; + f({{c}}); +} diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-ref2.C b/gcc/testsuite/g++.dg/cpp0x/initlist-ref2.C new file mode 100644 index 00000000000..401d868d820 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/initlist-ref2.C @@ -0,0 +1,10 @@ +// CWG1996 +// { dg-do compile { target c++11 } } + +struct S { operator struct D &(); } s; +D &d{s}; // OK, direct binding + +namespace N1 { + struct S { operator volatile struct D &(); } s; + const D &dr{s}; // { dg-error "invalid user-defined|discards qualifiers" } +} diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-ref3.C b/gcc/testsuite/g++.dg/cpp0x/initlist-ref3.C new file mode 100644 index 00000000000..e2cc1deace5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/initlist-ref3.C @@ -0,0 +1,13 @@ +// CWG1996 +// { dg-do compile { target c++11 } } + +struct D { constexpr D() {} } d; +struct S { + template <class T> + constexpr operator T& () const { return d; } +}; +constexpr S s; +constexpr const D &dr1(s); +static_assert (&dr1 == &d, ""); +constexpr const D &dr2{s}; +static_assert (&dr2 == &d, "");