Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

This tweak to CWG2369 has gotten more discussion lately in CWG, including in
P3606.  In those discussions, it occurred to me that having the check depend
on whether a class has been instantiated yet is unstable, that it should
only check for user-defined conversions.

Also, one commenter was surprised that adding an explicitly-declared default
constructor to a class changed things, so this patch also changes the
aggregate check to more narrowly checking for one-argument constructors
other than the copy/move constructors.

As a result, this early filter resembles how LOOKUP_DEFAULTED rejects any
candidate that would need a UDC: in both cases we want to avoid considering
arbitrary UDCs.  But here, rather than rejecting, we want the early filter
to let the candidate past without considering the conversion.

        PR c++/99599

gcc/cp/ChangeLog:

        * cp-tree.h (type_has_converting_constructor): Declare.
        * class.cc (type_has_converting_constructor): New.
        * pt.cc (conversion_may_instantiate_p): Don't check completeness.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp2a/concepts-recursive-sat4.C: Adjust again.
        * g++.dg/cpp2a/concepts-nondep5.C: New test.
---
 gcc/cp/cp-tree.h                              |  1 +
 gcc/cp/class.cc                               | 41 +++++++++++++++++++
 gcc/cp/pt.cc                                  | 40 +++++-------------
 gcc/testsuite/g++.dg/cpp2a/concepts-nondep5.C | 34 +++++++++++++++
 .../g++.dg/cpp2a/concepts-recursive-sat4.C    |  2 +-
 5 files changed, 88 insertions(+), 30 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-nondep5.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index a42c07a330b..175ab287490 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7056,6 +7056,7 @@ extern tree in_class_defaulted_default_constructor (tree);
 extern bool user_provided_p                    (tree);
 extern bool type_has_user_provided_constructor  (tree);
 extern bool type_has_non_user_provided_default_constructor (tree);
+extern bool type_has_converting_constructor    (tree);
 extern bool vbase_has_user_provided_move_assign (tree);
 extern tree default_init_uninitialized_part (tree);
 extern bool trivial_default_constructor_is_constexpr (tree);
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6767ac10358..370bfa35f9e 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -5724,6 +5724,47 @@ type_has_user_provided_constructor (tree t)
   return false;
 }
 
+/* Returns true iff class T has a constructor that accepts a single argument
+   and does not have a single parameter of type reference to T.
+
+   This does not exclude explicit constructors because they are still
+   considered for conversions within { } even though choosing one is
+   ill-formed.  */
+
+bool
+type_has_converting_constructor (tree t)
+{
+  if (!CLASS_TYPE_P (t))
+    return false;
+
+  if (!TYPE_HAS_USER_CONSTRUCTOR (t))
+    return false;
+
+  for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter)
+    {
+      tree fn = *iter;
+      tree parm = FUNCTION_FIRST_USER_PARMTYPE (fn);
+      if (parm == void_list_node
+         || !sufficient_parms_p (TREE_CHAIN (parm)))
+       /* Can't accept a single argument, so won't be considered for
+          conversion.  */
+       continue;
+      if (TREE_CODE (fn) == TEMPLATE_DECL
+         || TREE_CHAIN (parm) != void_list_node)
+       /* Not a simple single parameter.  */
+       return true;
+      if (TYPE_MAIN_VARIANT (non_reference (TREE_VALUE (parm)))
+         != DECL_CONTEXT (fn))
+       /* The single parameter has the wrong type.  */
+       return true;
+      if (get_constraints (fn))
+       /* Constrained.  */
+       return true;
+    }
+
+  return false;
+}
+
 /* Returns true iff class T has a user-provided or explicit constructor.  */
 
 bool
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 7b296d14a09..0694c28cde3 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -23501,9 +23501,13 @@ maybe_adjust_types_for_deduction (tree tparms,
   return result;
 }
 
-/* Return true if computing a conversion from FROM to TO might induce template
-   instantiation.  Conversely, if this predicate returns false then computing
-   the conversion definitely won't induce template instantiation.  */
+/* Return true if computing a conversion from FROM to TO might consider
+   user-defined conversions, which could lead to arbitrary template
+   instantiations (e.g. g++.dg/cpp2a/concepts-nondep1.C).  If this predicate
+   returns false then computing the conversion definitely won't try UDCs.
+
+   Note that this restriction parallels LOOKUP_DEFAULTED for CWG1092, but in
+   this case we want the early filter to pass instead of fail.  */
 
 static bool
 conversion_may_instantiate_p (tree to, tree from)
@@ -23511,36 +23515,14 @@ conversion_may_instantiate_p (tree to, tree from)
   to = non_reference (to);
   from = non_reference (from);
 
-  bool ptr_conv_p = false;
-  if (TYPE_PTR_P (to)
-      && TYPE_PTR_P (from))
-    {
-      to = TREE_TYPE (to);
-      from = TREE_TYPE (from);
-      ptr_conv_p = true;
-    }
-
-  /* If one of the types is a not-yet-instantiated class template
-     specialization, then computing the conversion might instantiate
-     it in order to inspect bases, conversion functions and/or
-     converting constructors.  */
-  if ((CLASS_TYPE_P (to)
-       && !COMPLETE_TYPE_P (to)
-       && CLASSTYPE_TEMPLATE_INSTANTIATION (to))
-      || (CLASS_TYPE_P (from)
-         && !COMPLETE_TYPE_P (from)
-         && CLASSTYPE_TEMPLATE_INSTANTIATION (from)))
-    return true;
-
-  /* Converting from one pointer type to another, or between
-     reference-related types, always yields a standard conversion.  */
-  if (ptr_conv_p || reference_related_p (to, from))
+  /* Converting between reference-related types is a standard conversion.  */
+  if (reference_related_p (to, from))
     return false;
 
   /* Converting to a non-aggregate class type will consider its
      user-declared constructors, which might induce instantiation.  */
   if (CLASS_TYPE_P (to)
-      && CLASSTYPE_NON_AGGREGATE (to))
+      && type_has_converting_constructor (to))
     return true;
 
   /* Similarly, converting from a class type will consider its conversion
@@ -23549,7 +23531,7 @@ conversion_may_instantiate_p (tree to, tree from)
       && TYPE_HAS_CONVERSION (from))
     return true;
 
-  /* Otherwise, computing this conversion definitely won't induce
+  /* Otherwise, computing this conversion won't risk arbitrary
      template instantiation.  */
   return false;
 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-nondep5.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-nondep5.C
new file mode 100644
index 00000000000..f08d4d424da
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-nondep5.C
@@ -0,0 +1,34 @@
+// The from_range_t default ctor should not break the PR99599 workaround
+// { dg-do compile { target c++20 } }
+
+template<typename T>
+struct S { T t; };
+
+template<typename T>
+concept C = sizeof(S<T>) > 0;
+
+struct I;
+
+struct from_range_t {
+    explicit from_range_t() = default;
+};
+inline constexpr from_range_t from_range;
+
+template<typename T>
+concept FromRange = __is_same_as (T, from_range_t);
+
+//#define WORKAROUND
+#ifdef WORKAROUND
+template<FromRange U, C T>
+void f(U, T*);
+#else
+template<C T>
+void f(from_range_t, T*);
+#endif
+
+void f(...);
+
+void g(I* p)
+{
+    ::f(0, p);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat4.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat4.C
index 08f206d3634..41364f7f05a 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat4.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat4.C
@@ -7,7 +7,7 @@ template <class T, class U> concept C = requires(T t, U u) { t 
* u; };
 // { dg-error "depends on itself" "" { target *-*-* } .-2 }
 
 template <class Rep> struct Int {
-  Int(); // make the class non-aggregate in light of PR99599 fix
+  Int(int = 0); // make the class ineligible for PR99599 workaround
   template <class T> requires C<T, Rep> friend void operator*(T, Int) { }
   template <class T> requires C<T, Rep> friend void operator*(Int, T) { }
 };

base-commit: d8dac49707e71844b4d1c21348d92addb19a0969
-- 
2.49.0

Reply via email to