[pushed] c++: fix explicit/copy problem [PR109247]

Message ID 20230602155349.1775177-1-jason@redhat.com
State Committed
Commit 9872d56661ade358c440914361c1ebdccd975bec
Headers
Series [pushed] c++: fix explicit/copy problem [PR109247] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Testing passed

Commit Message

Jason Merrill June 2, 2023, 3:53 p.m. UTC
  Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

In the testcase, the user wants the assignment to use the operator= declared
in the class, but because [over.match.list] says that explicit constructors
are also considered for list-initialization, as affirmed in CWG1228, we end
up choosing the implicitly-declared copy assignment operator, using the
explicit constructor template for the argument, which is ill-formed.  Other
implementations haven't implemented CWG1228, so we keep getting bug reports.

Discussion in CWG led to the idea for this targeted relaxation: if we use an
explicit constructor for the conversion to the argument of a copy or move
special member function, that makes the candidate worse than another.

	DR 2735
	PR c++/109247

gcc/cp/ChangeLog:

	* call.cc (sfk_copy_or_move): New.
	(joust): Add tiebreaker for explicit conv and copy ctor.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/initlist-explicit3.C: New test.
---
 gcc/cp/call.cc                                | 31 +++++++++++++++++++
 .../g++.dg/cpp0x/initlist-explicit3.C         | 15 +++++++++
 2 files changed, 46 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/initlist-explicit3.C


base-commit: 957798e44e7194f7b6a67b19f85ff72eab9a0d0e
  

Patch

diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 5d504e5c696..d6154f1a319 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -12611,6 +12611,17 @@  cand_parms_match (z_candidate *c1, z_candidate *c2)
   return compparms (parms1, parms2);
 }
 
+/* True iff FN is a copy or move constructor or assignment operator.  */
+
+static bool
+sfk_copy_or_move (tree fn)
+{
+  if (TREE_CODE (fn) != FUNCTION_DECL)
+    return false;
+  special_function_kind sfk = special_function_p (fn);
+  return sfk >= sfk_copy_constructor && sfk <= sfk_move_assignment;
+}
+
 /* Compare two candidates for overloading as described in
    [over.match.best].  Return values:
 
@@ -12910,6 +12921,26 @@  joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
 	return winner;
     }
 
+  /* CWG2735 (PR109247): A copy/move ctor/op= for which its operand uses an
+     explicit conversion (due to list-initialization) is worse.  */
+  {
+    z_candidate *sp = nullptr;
+    if (sfk_copy_or_move (cand1->fn))
+      sp = cand1;
+    if (sfk_copy_or_move (cand2->fn))
+      sp = sp ? nullptr : cand2;
+    if (sp)
+      {
+	conversion *conv = sp->convs[!DECL_CONSTRUCTOR_P (sp->fn)];
+	if (conv->user_conv_p)
+	  for (; conv; conv = next_conversion (conv))
+	    if (conv->kind == ck_user
+		&& DECL_P (conv->cand->fn)
+		&& DECL_NONCONVERTING_P (conv->cand->fn))
+	      return (sp == cand1) ? -1 : 1;
+      }
+  }
+
   /* or, if not that,
      F1 is a non-template function and F2 is a template function
      specialization.  */
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-explicit3.C b/gcc/testsuite/g++.dg/cpp0x/initlist-explicit3.C
new file mode 100644
index 00000000000..b0c92784566
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist-explicit3.C
@@ -0,0 +1,15 @@ 
+// PR c++/109247
+// { dg-do compile { target c++11 } }
+
+template <typename _Tp> struct optional {
+  template <typename _Up> explicit optional(_Up);
+  template <typename _Up = _Tp> void operator=(_Up);
+};
+int setPattern_pattern;
+struct SourceBrush {
+  struct Brush {
+    int brush;
+  };
+  void setPattern() { m_brush = {setPattern_pattern}; }
+  optional<Brush> m_brush;
+};