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