In the testcase below, we're overeagerly checking the constraints on
the conversion function B<int>::operator bool() as part of finding an
implicit conversion sequence from B<int> to const A&.

This behavior seems to be nonconforming because according to
[over.match.copy] and [over.match.conv], only those conversion functions
which yield a type that can be converted to the target type via a
standard conversion sequence are candidate functions, and according to
[over.match.viable], only candidate functions get their constraints
checked.

This patch fixes this by swapping the order of the calls to
add_candidates and implicit_conversion in build_user_type_conversion_1
when the conversion function has a non-dependent result type.
(Conversion function templates with a dependent result type already get
handled appropriately in add_template_candidate, it seems.)

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK fo
trunk?

gcc/cp/ChangeLog:

        * call.c (build_user_type_conversion_1): When the conversion
        function has a non-dependent result type, pre-check the
        conversion sequence that would follow the conversion function.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp2a/concepts-conv3.C: New test.
---
 gcc/cp/call.c                               | 33 +++++++++++++++++----
 gcc/testsuite/g++.dg/cpp2a/concepts-conv3.C | 14 +++++++++
 2 files changed, 41 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-conv3.C

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index bd662518958..6d955ea083f 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4099,6 +4099,23 @@ build_user_type_conversion_1 (tree totype, tree expr, 
int flags,
       if (TYPE_REF_P (totype))
        convflags |= LOOKUP_NO_TEMP_BIND;
 
+      conversion *pre_ics = NULL;
+      if (!uses_template_parms (TREE_TYPE (conv_fns)))
+       {
+         /* A conversion function is a candidate function only if it
+            yields a type that can be converted to the target type.
+            We check this for conversion functions with non-dependent
+            result type now to disregard the non-candidate functions
+            before calling add_candidates.  */
+         pre_ics = implicit_conversion (totype,
+                                        TREE_TYPE (conv_fns),
+                                        0,
+                                        /*c_cast_p=*/false, convflags,
+                                        complain);
+         if (!pre_ics)
+           continue;
+       }
+
       old_candidates = candidates;
       add_candidates (TREE_VALUE (conv_fns), first_arg, NULL, totype,
                      NULL_TREE, false,
@@ -4112,12 +4129,16 @@ build_user_type_conversion_1 (tree totype, tree expr, 
int flags,
            continue;
 
          tree rettype = TREE_TYPE (TREE_TYPE (cand->fn));
-         conversion *ics
-           = implicit_conversion (totype,
-                                  rettype,
-                                  0,
-                                  /*c_cast_p=*/false, convflags,
-                                  complain);
+         conversion *ics;
+         if (pre_ics)
+           /* We've already computed the conversion sequence above.  */
+           ics = pre_ics;
+         else
+           ics = implicit_conversion (totype,
+                                      rettype,
+                                      0,
+                                      /*c_cast_p=*/false, convflags,
+                                      complain);
 
          /* If LOOKUP_NO_TEMP_BIND isn't set, then this is
             copy-initialization.  In that case, "The result of the
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-conv3.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-conv3.C
new file mode 100644
index 00000000000..bcbc08fe869
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-conv3.C
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++20 } }
+
+template <class T>
+struct Error { static constexpr auto value = T::value; };
+
+struct A { A(const A&); };
+
+template <class T>
+struct B { operator bool() requires Error<T>::value; };
+
+template <class T>
+concept C = requires (B<T> b) { A(b); };
+
+static_assert(!C<int>);
-- 
2.29.0.rc0

Reply via email to