fmt icon indicating copy to clipboard operation
fmt copied to clipboard

Test `enforce-checks-test.cc` fails after implementing P2499

Open strega-nil-ms opened this issue 3 years ago • 1 comments

Hi! Friendly neighborhood STL implementor here 😸

After implementing P2499R0 in microsoft/stl#2947, the test enforce-checks-test now fails.

Compiler output
C:\Users\nimazzuc\projects\fmt\include\fmt\core.h(2742): error C2039: 'parse': is not a member of 'fmt::v9::detail::fallback_formatter<stripped_type,char_type,void>'
C:\Users\nimazzuc\projects\fmt\include\fmt\core.h(2741): note: see declaration of 'fmt::v9::detail::fallback_formatter<stripped_type,char_type,void>'
C:\Users\nimazzuc\projects\fmt\include\fmt\core.h(2947): note: see reference to function template instantiation 'const char *fmt::v9::detail::parse_format_specs<std::vector<char,std::allocator<char>>,fmt::v9::detail::compile_parse_context<Char,ErrorHandler>>(ParseContext &)' being compiled
        with
        [
            Char=char,
            ErrorHandler=fmt::v9::detail::error_handler,
            ParseContext=fmt::v9::detail::compile_parse_context<char,fmt::v9::detail::error_handler>
        ]
C:\Users\nimazzuc\projects\fmt\include\fmt\core.h(2947): note: while compiling class template member function 'fmt::v9::detail::format_string_checker<Char,fmt::v9::detail::error_handler,std::vector<char,std::allocator<char>>>::format_string_checker(fmt::v9::basic_string_view<Char>,ErrorHandler)'
        with
        [
            Char=char,
            ErrorHandler=fmt::v9::detail::error_handler
        ]
C:\Users\nimazzuc\projects\fmt\include\fmt\core.h(3141): note: see reference to class template instantiation 'fmt::v9::detail::format_string_checker<Char,fmt::v9::detail::error_handler,std::vector<char,std::allocator<char>>>' being compiled
        with
        [
            Char=char
        ]
.\test\enforce-checks-test.cc(55): note: see reference to function template instantiation 'fmt::v9::basic_format_string<char,std::vector<char,std::allocator<char>> &>::basic_format_string<test_range::<lambda_1>::()::FMT_COMPILE_STRING,0>(const S &)' being compiled
        with
        [
            S=test_range::<lambda_1>::()::FMT_COMPILE_STRING
        ]

Before, it worked because basic_string_view<char> was convertible from any contiguous range of char; now, it is only constructible from any contiguous range of char.

strega-nil-ms avatar Aug 09 '22 19:08 strega-nil-ms

This is surprising because vector<char> doesn't need to be convertible to string_view to be formattable as a range of chars. See e.g. https://godbolt.org/z/4oPK6dxM5.

vitaut avatar Aug 09 '22 23:08 vitaut

Yeah, I dunno; I think the metaprogramming is broken somehow, but it slipped under the radar because vector<char> was convertible to string_view

strega-nil-ms avatar Aug 10 '22 16:08 strega-nil-ms

Any updates or patch for this?

QuellaZhang avatar Aug 22 '22 08:08 QuellaZhang

I don't have a windows machine at hand and the issue doesn't repro on godbolt. If someone is able to repro and can investigate this issue it would be appreciated.

vitaut avatar Aug 22 '22 17:08 vitaut

So, interestingly, it does work for vector<int>, but not for vector<char>:

#include <fmt/format.h>
#include <fmt/ranges.h>
#include <vector>

using namespace std;

int main() {
  vector<CHAR_OR_INT> hello{'h', 'e', 'l', 'l', 'o'};
  fmt::print("{}\n", hello);
}

works when -DCHAR_OR_INT=int, but doesn't when -DCHAR_OR_INT=char

strega-nil-ms avatar Aug 23 '22 21:08 strega-nil-ms

It looks like vector<char> is not considered a range for some reason:

#include <fmt/format.h>
#include <fmt/ranges.h>
#include <vector>

using namespace std;

int main() {
  static_assert(fmt::is_range<vector<char>, char>::value); // FAILS
  static_assert(fmt::is_range<vector<int>, char>::value); // SUCCEEDS
}

strega-nil-ms avatar Aug 23 '22 21:08 strega-nil-ms

I think I figured out the problem:

template <typename T, typename Char> struct is_range {
  static constexpr const bool value =
      detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
      !std::is_convertible<T, std::basic_string<Char>>::value &&
      !std::is_constructible<detail::std_string_view<Char>, T>::value; // <--- problem
};

string_view is constructible from vector<char>, but vector<char> is not implicitly convertible to string_view; therefore, vector<char> is not a range, but because it's not convertible, it doesn't get formatted as a string_view either.

(I think this may be the issue in #3050)

strega-nil-ms avatar Aug 23 '22 22:08 strega-nil-ms

Thanks for investigating, @strega-nil-ms. Does https://github.com/fmtlib/fmt/commit/e4f4fc77884196163b45b528875bf65d1c9b922e fix the issue?

vitaut avatar Aug 26 '22 14:08 vitaut

@vitaut it should, but it's still incorrect - what you have is

   static constexpr const bool value =
       detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
       !std::is_convertible<T, std::basic_string<Char>>::value &&
-      !std::is_constructible<detail::std_string_view<Char>, T>::value;
+      !std::is_convertible<detail::std_string_view<Char>, T>::value;

but what you want is

   static constexpr const bool value =
       detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
       !std::is_convertible<T, std::basic_string<Char>>::value &&
-      !std::is_constructible<detail::std_string_view<Char>, T>::value;
+      !std::is_convertible<T, detail::std_string_view<Char>>::value;

note the different parameter ordering of is_constructible and is_convertible

strega-nil-ms avatar Aug 26 '22 20:08 strega-nil-ms

the different parameter ordering of is_constructible and is_convertible

Ugh.

Thanks!

vitaut avatar Aug 26 '22 20:08 vitaut

Should be fixed in https://github.com/fmtlib/fmt/commit/4a8e2949bb8d803a7607b62b632ec6d8669b7995.

vitaut avatar Aug 26 '22 21:08 vitaut