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.

Reply via email to