diff mbox series

[pushed] c++: reference list-init, conversion fn [PR113141]

Message ID 20240412192835.2466989-1-jason@redhat.com
State New
Headers show
Series [pushed] c++: reference list-init, conversion fn [PR113141] | expand

Commit Message

Jason Merrill April 12, 2024, 7:27 p.m. UTC
Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

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 <ppalka@redhat.com>
---
 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(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/initlist-ref1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/initlist-ref2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/initlist-ref3.C


base-commit: 0fd824d717ca901319864a5eeba4e62b278f8329
diff mbox series

Patch

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, "");