c++: Avoid considering some conversion ops [PR97600]
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.
This commit is contained in:
parent
8bab7dce62
commit
9324f7a25c
|
@ -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
|
||||
|
|
|
@ -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>);
|
|
@ -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>);
|
Loading…
Reference in New Issue