https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106784
Jonathan Wakely <redi at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |jason at gcc dot gnu.org --- Comment #6 from Jonathan Wakely <redi at gcc dot gnu.org> --- I'm seeing a regression in 21_strings/basic_string/types/1.cc when I start using __is_convertible in the library. Reduced to: template<bool B> struct bool_constant { static constexpr bool value = B; }; using true_type = bool_constant<true>; using false_type = bool_constant<false>; template<typename T> struct is_void : false_type { }; template<> struct is_void<void> : true_type { }; template<typename T> T&& declval(); template<bool> struct enable_if { }; template<> struct enable_if<true> { using type = void; }; template<bool B> using enable_if_t = typename enable_if<B>::type; #ifdef USE_BUILTIN template<typename _From, typename _To> struct is_convertible : public bool_constant<__is_convertible(_From, _To)> { }; #else template<typename _From, typename _To, bool = is_void<_From>::value> struct __is_convertible_helper { typedef typename is_void<_To>::type type; }; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wctor-dtor-privacy" template<typename _From, typename _To> class __is_convertible_helper<_From, _To, false> { template<typename _To1> static void __test_aux(_To1) noexcept; template<typename _From1, typename _To1, typename = decltype(__test_aux<_To1>(declval<_From1>()))> static true_type __test(int); template<typename, typename> static false_type __test(...); public: typedef decltype(__test<_From, _To>(0)) type; }; #pragma GCC diagnostic pop /// is_convertible template<typename _From, typename _To> struct is_convertible : public __is_convertible_helper<_From, _To>::type { }; #endif template<class CharT> struct char_traits { static unsigned long length(const char* s) { eq(*s, *s); return 0; } static void eq(CharT l, CharT r) noexcept { l.f(r); } }; template<class CharT> struct basic_string_view { using traits_type = char_traits<CharT>; constexpr basic_string_view() = default; constexpr basic_string_view(const basic_string_view&) = default; constexpr basic_string_view(const CharT* __str) noexcept : _M_len{traits_type::length(__str)} { } unsigned long _M_len = 0; }; template<class CharT> struct basic_string { template<class T> enable_if_t<is_convertible<const T&, basic_string_view<CharT>>::value && !is_convertible<const T&, const char*>::value> replace(int, T) { } void replace(unsigned long, const char*) { } void replace(const char* s) { replace(1, s); } }; int main() { basic_string<char> s; s.replace(""); } This is fine using the library trait, but compiled with -DUSE_BUILTIN it fails: conv.cc: In instantiation of 'static void char_traits<CharT>::eq(CharT, CharT) [with CharT = char]': conv.cc:62:50: required from 'static long unsigned int char_traits<CharT>::length(const char*) [with CharT = char]' conv.cc:77:31: required from 'constexpr basic_string_view<CharT>::basic_string_view(const CharT*) [with CharT = char]' conv.cc:18:28: required from 'struct is_convertible<const char* const&, basic_string_view<char> >' conv.cc:87:69: required by substitution of 'template<class T> enable_if_t<(is_convertible<const T&, basic_string_view<char> >::value && (! is_convertible<const T&, const char*>::value))> basic_string<char>::replace(int, T) [with T = const char*]' conv.cc:93:40: required from 'void basic_string<CharT>::replace(const char*) [with CharT = char]' conv.cc:99:12: required from here conv.cc:64:49: error: request for member 'f' in 'l', which is of non-class type 'char' 64 | static void eq(CharT l, CharT r) noexcept { l.f(r); } | ~~^ The error is correct, char_traits<char>::eq is ill-formed. Strictly speaking, the library testcase is invalid for basic_string, the N::X type needs to be equality comparable. But I don't think __is_convertible should be detecting that. To determine if const char* is convertible to basic_string_view(const char*) should just need to examine the signature, and not instantiate the body of the function. I think it probably instantiates it eagerly because it's constexpr. IIRC Jason made some changes to template substitution to prevent such eager instantiations of constexpr functions. Maybe something like that is currently missing from __is_convertible? Or maybe the trait is fine, and the code above is just invalid. I can easily fix the library test that fails, but I think it's worth checking first whether something in __is_convertible needs a fix.