public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [pushed] c++: Avoid considering some conversion ops [PR97600]
@ 2020-12-11 20:41 Jason Merrill
  0 siblings, 0 replies; only message in thread
From: Jason Merrill @ 2020-12-11 20:41 UTC (permalink / raw)
  To: gcc-patches

Patrick's earlier patch to check convertibility before constraints for
conversion ops wasn't suitable because checking convertibility can also lead
to unwanted instantiations, but it occurs to me that there's a smaller check
we can do to avoid doing normal consideration of the conversion ops in this
case: since we're in the middle of a user-defined conversion, we can exclude
from consideration any conversion ops that return a type that would need an
additional user-defined conversion to reach the desired type: namely, a type
that differs in class-ness from the desired type.

[temp.inst]/9 allows optimizations like this: "If the function selected by
overload resolution can be determined without instantiating a class template
definition, it is unspecified whether that instantiation actually takes
place."

gcc/cp/ChangeLog:

	PR libstdc++/97600
	* call.c (build_user_type_conversion_1): Avoid considering
	conversion functions that return a clearly unsuitable type.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-conv3.C: New test.
---
 gcc/cp/call.c                                | 18 +++++++++++++-
 gcc/testsuite/g++.dg/cpp2a/concepts-conv3.C  | 25 ++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/concepts-conv3a.C | 17 +++++++++++++
 3 files changed, 59 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-conv3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-conv3a.C

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 221e3de0c70..c2d62e582bf 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4025,9 +4025,9 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags,
        creating a garbage BASELINK; constructors can't be inherited.  */
     ctors = get_class_binding (totype, complete_ctor_identifier);
 
+  tree to_nonref = non_reference (totype);
   if (MAYBE_CLASS_TYPE_P (fromtype))
     {
-      tree to_nonref = non_reference (totype);
       if (same_type_ignoring_top_level_qualifiers_p (to_nonref, fromtype) ||
 	  (CLASS_TYPE_P (to_nonref) && CLASS_TYPE_P (fromtype)
 	   && DERIVED_FROM_P (to_nonref, fromtype)))
@@ -4111,6 +4111,22 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags,
       tree conversion_path = TREE_PURPOSE (conv_fns);
       struct z_candidate *old_candidates;
 
+      /* If LOOKUP_NO_CONVERSION, don't consider a conversion function that
+	 would need an addional user-defined conversion, i.e. if the return
+	 type differs in class-ness from the desired type.  So we avoid
+	 considering operator bool when calling a copy constructor.
+
+	 This optimization avoids the failure in PR97600, and is allowed by
+	 [temp.inst]/9: "If the function selected by overload resolution can be
+	 determined without instantiating a class template definition, it is
+	 unspecified whether that instantiation actually takes place."	*/
+      tree convtype = non_reference (TREE_TYPE (conv_fns));
+      if ((flags & LOOKUP_NO_CONVERSION)
+	  && !WILDCARD_TYPE_P (convtype)
+	  && (CLASS_TYPE_P (to_nonref)
+	      != CLASS_TYPE_P (convtype)))
+	continue;
+
       /* If we are called to convert to a reference type, we are trying to
 	 find a direct binding, so don't even consider temporaries.  If
 	 we don't find a direct binding, the caller will try again to
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..d53f37c10e6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-conv3.C
@@ -0,0 +1,25 @@
+// { dg-do compile { target c++20 } }
+
+// Here, normal overload resolution would consider B::operator bool when
+// evaluating A(b), leading to a hard error instantiating Error<int>, but we
+// avoid considering it by noticing that converting bool (a scalar) to A (a
+// class) would require a user-defined conversion, which is not allowed when
+// we're already dealing with the user-defined conversion to A.
+
+// This seems to be allowed by [temp.inst]/9: "If the function selected by
+// overload resolution (12.4) can be determined without instantiating a class
+// template definition, it is unspecified whether that instantiation actually
+// takes place."
+
+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>);
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-conv3a.C b/gcc/testsuite/g++.dg/cpp2a/concepts-conv3a.C
new file mode 100644
index 00000000000..7e9e28474d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-conv3a.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target c++20 } }
+
+// But make sure we do consider template conversions that could produce the
+// right type.
+
+template <class T>
+struct Error { static constexpr auto value = T::value; }; // { dg-error "not a member" }
+
+struct A { A(const A&); };
+
+template <class T>
+struct B { template <class U> operator U() requires Error<T>::value; };
+
+template <class T>
+concept C = requires (B<T> b) { A(b); }; // { dg-message "required from here" }
+
+static_assert(!C<int>);

base-commit: 8bab7dce62e321e722346ba2db2d381bdf3fbe4b
-- 
2.27.0


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-12-11 20:41 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-11 20:41 [pushed] c++: Avoid considering some conversion ops [PR97600] Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).