Skip to content

[clang] 21.1.0 C++20 regression "no matching conversion for functional-style cast" for template specialization #157933

@Cazadorro

Description

@Cazadorro

The following code worked in previous versions of clang, and works in MSVC, and GCC, but fails in Clang 21.1.0, this code attempts to specialize the function "set_data" via SFINAE with requires for scalars, std::span using a special "ns_is_specialization_of" macro with links to where this was taken from, and explicit specialization for std::string_view and std::string

#include <type_traits> 
#include <string_view>
#include <span> 
#include <iostream> 
#include <cstdint> 

namespace ns{
//From https://stackoverflow.com/a/55398444/, only source where I could find that handles non type templates
    namespace detail {
        template<auto f>
        struct is_specialization_of_impl {
        private:
            template<class T>
            static auto value_impl(int) -> std::is_same<T, decltype(f.template operator()<T>())>;

            template<class T>
            static auto value_impl(...) -> std::false_type;

        public:
            template<class T>
            static constexpr bool value = decltype(value_impl<T>(0))::value;
        };

// To replace std::declval which yields T&&
        template<class T>
        T declrval();
    }
}
#define ns_is_specialization_of(...) \
     ns::detail::is_specialization_of_impl<[]<class NsImplTemplate_T>() -> decltype(__VA_ARGS__(ns::detail::declrval<NsImplTemplate_T>())) { }>::template value

template <typename T>
void set_data(std::string_view key, T values) = delete;
void set_data(std::string_view key, const void* value, std::size_t value_count){
    std::cout << key << (uintptr_t)(value) << value_count; 
}

//attempting to specialize set_data for scalars, std::span, string_view, and string all separatley
template <typename T>
    requires(ns_is_specialization_of(std::span)<T>)
void set_data(std::string_view key, T values) {
    
    set_data(key, values.data(), values.size());
}

template <typename T>
    requires std::integral<T>
    || std::floating_point<T>
    || std::same_as<std::remove_cvref<T>(), bool>
void set_data(std::string_view key, T values) {
    set_data(key, &values, 1);
}
template <>
inline void set_data(std::string_view key, std::string_view value) {

    set_data(key, value.data(), value.size());
}

template <>
inline void set_data(std::string_view key, std::string value) {
    // verify_data_type<std::string>(key, value.size());
    set_data(key, value.data(), value.size());
}

int test_function(int num) {
    std::span<char> test; 
    set_data("hello", test);
    return num * num;
}

This results in the rather confusing error:

<source>:40:38: error: no matching conversion for functional-style cast from 'std::basic_string<char>' to 'std::span<remove_reference_t<ranges::range_reference_t<std::basic_string<char, std::char_traits<char>, std::allocator<char>> &>>>' (aka 'span<char>')
   40 |     requires(ns_is_specialization_of(std::span)<T>)
      |              ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
<source>:30:85: note: expanded from macro 'ns_is_specialization_of'
   30 |      ns::detail::is_specialization_of_impl<[]<class NsImplTemplate_T>() -> decltype(__VA_ARGS__(ns::detail::declrval<NsImplTemplate_T>())) { }>::template value
      |                                                                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:40:14: note: in instantiation of member class '(unnamed class at <source>:40:14)' requested here
   40 |     requires(ns_is_specialization_of(std::span)<T>)
      |              ^
<source>:30:44: note: expanded from macro 'ns_is_specialization_of'
   30 |      ns::detail::is_specialization_of_impl<[]<class NsImplTemplate_T>() -> decltype(__VA_ARGS__(ns::detail::declrval<NsImplTemplate_T>())) { }>::template value
      |                                            ^
<source>:21:52: note: while substituting explicitly-specified template arguments into function template 'value_impl' 
   21 |             static constexpr bool value = decltype(value_impl<T>(0))::value;
      |                                                    ^
<source>:40:14: note: in instantiation of static data member 'ns::detail::is_specialization_of_impl<(lambda at <source>:40:14){}>::value<std::basic_string<char>>' requested here
   40 |     requires(ns_is_specialization_of(std::span)<T>)
      |              ^
<source>:30:155: note: expanded from macro 'ns_is_specialization_of'
   30 |      ns::detail::is_specialization_of_impl<[]<class NsImplTemplate_T>() -> decltype(__VA_ARGS__(ns::detail::declrval<NsImplTemplate_T>())) { }>::template value
      |                                                                                                                                                           ^
<source>:40:14: note: while substituting template arguments into constraint expression here
   40 |     requires(ns_is_specialization_of(std::span)<T>)
      |              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:30:6: note: expanded from macro 'ns_is_specialization_of'
   30 |      ns::detail::is_specialization_of_impl<[]<class NsImplTemplate_T>() -> decltype(__VA_ARGS__(ns::detail::declrval<NsImplTemplate_T>())) { }>::template value
      |      ^
<source>:60:13: note: while checking constraint satisfaction for template 'set_data<std::basic_string<char>>' required here
   60 | inline void set_data(std::string_view key, std::string value) {
      |             ^~~~~~~~
<source>:60:13: note: while substituting deduced template arguments into function template 'set_data' [with T = std::string]
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:248:7: note: candidate constructor not viable: no known conversion from 'std::basic_string<char>' to 'const span<char>' for 1st argument
  248 |       span(const span&) noexcept = default;
      |       ^    ~~~~~~~~~~~
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:203:2: note: candidate template ignored: could not match 'type_identity_t<element_type>[_ArrayExtent]' (aka 'char[_ArrayExtent]') against 'std::basic_string<char>'
  203 |         span(type_identity_t<element_type> (&__arr)[_ArrayExtent]) noexcept
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:210:2: note: candidate template ignored: could not match 'array' against 'std::basic_string'
  210 |         span(array<_Tp, _ArrayExtent>& __arr) noexcept
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:217:2: note: candidate template ignored: could not match 'array' against 'std::basic_string'
  217 |         span(const array<_Tp, _ArrayExtent>& __arr) noexcept
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:229:2: note: candidate template ignored: constraints not satisfied [with _Range = std::basic_string<char>]
  229 |         span(_Range&& __range)
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:226:8: note: because 'std::basic_string<char>' does not satisfy 'borrowed_range'
  226 |           && (ranges::borrowed_range<_Range> || is_const_v<element_type>)
      |               ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/bits/ranges_base.h:523:23: note: because 'std::basic_string<char>' does not satisfy '__maybe_borrowed_range'
  523 |       = range<_Tp> && __detail::__maybe_borrowed_range<_Tp>;
      |                       ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/bits/ranges_base.h:101:4: note: because 'is_lvalue_reference_v<std::basic_string<char>>' evaluated to false
  101 |         = is_lvalue_reference_v<_Tp>
      |           ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/bits/ranges_base.h:102:7: note: and 'enable_borrowed_range<remove_cvref_t<std::basic_string<char, std::char_traits<char>, std::allocator<char>>>>' evaluated to false
  102 |           || enable_borrowed_range<remove_cvref_t<_Tp>>;
      |              ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:226:42: note: and 'is_const_v<element_type>' evaluated to false
  226 |           && (ranges::borrowed_range<_Range> || is_const_v<element_type>)
      |                                                 ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:256:2: note: candidate template ignored: could not match 'span' against 'std::basic_string'
  256 |         span(const span<_OType, _OExtent>& __s) noexcept
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:470:7: note: candidate constructor not viable: constraints not satisfied
  470 |       span(_SizedPtr __ptr) noexcept
      |       ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:471:17: note: because 'extent != dynamic_extent' (18446744073709551615 != 18446744073709551615) evaluated to false
  471 |       requires (extent != dynamic_extent)
      |                 ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:177:7: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
  177 |       span() noexcept
      |       ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:185:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
  185 |         span(_It __first, size_type __count)
      |         ^    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:194:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
  194 |         span(_It __first, _End __last)
      |         ^    ~~~~~~~~~~~~~~~~~~~~~~~~
<source>:40:38: error: no matching conversion for functional-style cast from 'std::basic_string<char>' to 'std::span<remove_reference_t<ranges::range_reference_t<std::basic_string<char, std::char_traits<char>, std::allocator<char>> &>>>' (aka 'span<char>')
   40 |     requires(ns_is_specialization_of(std::span)<T>)
      |              ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
<source>:30:85: note: expanded from macro 'ns_is_specialization_of'
   30 |      ns::detail::is_specialization_of_impl<[]<class NsImplTemplate_T>() -> decltype(__VA_ARGS__(ns::detail::declrval<NsImplTemplate_T>())) { }>::template value
      |                                                                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:248:7: note: candidate constructor not viable: no known conversion from 'std::basic_string<char>' to 'const span<char>' for 1st argument
  248 |       span(const span&) noexcept = default;
      |       ^    ~~~~~~~~~~~
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:203:2: note: candidate template ignored: could not match 'type_identity_t<element_type>[_ArrayExtent]' (aka 'char[_ArrayExtent]') against 'std::basic_string<char>'
  203 |         span(type_identity_t<element_type> (&__arr)[_ArrayExtent]) noexcept
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:210:2: note: candidate template ignored: could not match 'array' against 'std::basic_string'
  210 |         span(array<_Tp, _ArrayExtent>& __arr) noexcept
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:217:2: note: candidate template ignored: could not match 'array' against 'std::basic_string'
  217 |         span(const array<_Tp, _ArrayExtent>& __arr) noexcept
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:229:2: note: candidate template ignored: constraints not satisfied [with _Range = std::basic_string<char>]
  229 |         span(_Range&& __range)
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:226:8: note: because 'std::basic_string<char>' does not satisfy 'borrowed_range'
  226 |           && (ranges::borrowed_range<_Range> || is_const_v<element_type>)
      |               ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/bits/ranges_base.h:523:23: note: because 'std::basic_string<char>' does not satisfy '__maybe_borrowed_range'
  523 |       = range<_Tp> && __detail::__maybe_borrowed_range<_Tp>;
      |                       ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/bits/ranges_base.h:101:4: note: because 'is_lvalue_reference_v<std::basic_string<char>>' evaluated to false
  101 |         = is_lvalue_reference_v<_Tp>
      |           ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/bits/ranges_base.h:102:7: note: and 'enable_borrowed_range<remove_cvref_t<std::basic_string<char, std::char_traits<char>, std::allocator<char>>>>' evaluated to false
  102 |           || enable_borrowed_range<remove_cvref_t<_Tp>>;
      |              ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:226:42: note: and 'is_const_v<element_type>' evaluated to false
  226 |           && (ranges::borrowed_range<_Range> || is_const_v<element_type>)
      |                                                 ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:256:2: note: candidate template ignored: could not match 'span' against 'std::basic_string'
  256 |         span(const span<_OType, _OExtent>& __s) noexcept
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:470:7: note: candidate constructor not viable: constraints not satisfied
  470 |       span(_SizedPtr __ptr) noexcept
      |       ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:471:17: note: because 'extent != dynamic_extent' (18446744073709551615 != 18446744073709551615) evaluated to false
  471 |       requires (extent != dynamic_extent)
      |                 ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:177:7: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
  177 |       span() noexcept
      |       ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:185:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
  185 |         span(_It __first, size_type __count)
      |         ^    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:194:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
  194 |         span(_It __first, _End __last)
      |         ^    ~~~~~~~~~~~~~~~~~~~~~~~~
<source>:40:38: error: no matching conversion for functional-style cast from 'std::basic_string<char>' to 'std::span<remove_reference_t<ranges::range_reference_t<std::basic_string<char, std::char_traits<char>, std::allocator<char>> &>>>' (aka 'span<char>')
   40 |     requires(ns_is_specialization_of(std::span)<T>)
      |              ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
<source>:30:85: note: expanded from macro 'ns_is_specialization_of'
   30 |      ns::detail::is_specialization_of_impl<[]<class NsImplTemplate_T>() -> decltype(__VA_ARGS__(ns::detail::declrval<NsImplTemplate_T>())) { }>::template value
      |                                                                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:248:7: note: candidate constructor not viable: no known conversion from 'std::basic_string<char>' to 'const span<char>' for 1st argument
  248 |       span(const span&) noexcept = default;
      |       ^    ~~~~~~~~~~~
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:203:2: note: candidate template ignored: could not match 'type_identity_t<element_type>[_ArrayExtent]' (aka 'char[_ArrayExtent]') against 'std::basic_string<char>'
  203 |         span(type_identity_t<element_type> (&__arr)[_ArrayExtent]) noexcept
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:210:2: note: candidate template ignored: could not match 'array' against 'std::basic_string'
  210 |         span(array<_Tp, _ArrayExtent>& __arr) noexcept
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:217:2: note: candidate template ignored: could not match 'array' against 'std::basic_string'
  217 |         span(const array<_Tp, _ArrayExtent>& __arr) noexcept
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:229:2: note: candidate template ignored: constraints not satisfied [with _Range = std::basic_string<char>]
  229 |         span(_Range&& __range)
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:226:8: note: because 'std::basic_string<char>' does not satisfy 'borrowed_range'
  226 |           && (ranges::borrowed_range<_Range> || is_const_v<element_type>)
      |               ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/bits/ranges_base.h:523:23: note: because 'std::basic_string<char>' does not satisfy '__maybe_borrowed_range'
  523 |       = range<_Tp> && __detail::__maybe_borrowed_range<_Tp>;
      |                       ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/bits/ranges_base.h:101:4: note: because 'is_lvalue_reference_v<std::basic_string<char>>' evaluated to false
  101 |         = is_lvalue_reference_v<_Tp>
      |           ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/bits/ranges_base.h:102:7: note: and 'enable_borrowed_range<remove_cvref_t<std::basic_string<char, std::char_traits<char>, std::allocator<char>>>>' evaluated to false
  102 |           || enable_borrowed_range<remove_cvref_t<_Tp>>;
      |              ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:226:42: note: and 'is_const_v<element_type>' evaluated to false
  226 |           && (ranges::borrowed_range<_Range> || is_const_v<element_type>)
      |                                                 ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:256:2: note: candidate template ignored: could not match 'span' against 'std::basic_string'
  256 |         span(const span<_OType, _OExtent>& __s) noexcept
      |         ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:470:7: note: candidate constructor not viable: constraints not satisfied
  470 |       span(_SizedPtr __ptr) noexcept
      |       ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:471:17: note: because 'extent != dynamic_extent' (18446744073709551615 != 18446744073709551615) evaluated to false
  471 |       requires (extent != dynamic_extent)
      |                 ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:177:7: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
  177 |       span() noexcept
      |       ^
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:185:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
  185 |         span(_It __first, size_type __count)
      |         ^    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-15.2.0/lib/gcc/x86_64-linux-gnu/15.2.0/../../../../include/c++/15.2.0/span:194:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
  194 |         span(_It __first, _End __last)
      |         ^    ~~~~~~~~~~~~~~~~~~~~~~~~
3 errors generated.
Compiler returned: 1

Godbolt Links:

Fails Clang21.1.0

Succeeds: Clang20.1.0

Succeeds: msvc v19.43 VS17.13

Succeeds: GCC 15.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    clang:frontendLanguage frontend issues, e.g. anything involving "Sema"conceptsC++20 conceptsneeds-reductionLarge reproducer that should be reduced into a simpler form

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions