Recently (via commit d2a2320), the existing formatter specialization for ranges has been made more robust by introduction of short-circuiting meta-programming logical functions. Even though this is an improvement in the existing code base that cannot rely on C++ language concepts, this is still not enough. This issue is opened to suggest to introduce a non-concept-based compile-time barrier against recursive range types (Such as std::filesystem::path or boost::filesystem::path), corresponding to the specialization suggested in the C++ proposal P2286R8.
The reason for this need can be easily explained by observing that on Windows's systems fmt::is_range returns true for char, see https://godbolt.org/z/88bGdh14r (and on posix systems it returns true for wchar_t, see https://godbolt.org/z/8e5WEh45Y), that means that while the compiler attempts to find out whether the partial specialization in
|
struct formatter<std::filesystem::path, Char> |
is better than the partial specialization in
the value for the
Enabler parameter is evaluated. If
fmt::is_range returns
true, this means that it is guaranteed that also the additional part of the test,
|
is_formattable<detail::uncvref_type<detail::maybe_const_range<R>>, |
|
Char>, |
|
detail::has_fallback_formatter< |
|
detail::uncvref_type<detail::maybe_const_range<R>>, Char> |
is also evaluated and this can lead to an infinite compiler recursion, which can be observed, when attempting to evaluate that part of the expression in an isolated program (For the analysis, we can exclude the influence of the second part involving
detail::has_fallback_formatter, because this evaluates now by default to a constant value), such as the following one:
#include <fmt/ranges.h>
#include <filesystem>
static_assert(fmt::is_range<std::filesystem::path, char>::value);
static_assert(!fmt::is_range<std::filesystem::path, wchar_t>::value);
bool foo() {
using R = std::filesystem::path;
constexpr bool ret = fmt::is_formattable<fmt::detail::uncvref_type<
fmt::detail::maybe_const_range<R>>,
char>::value;
return ret;
}
which results in the following compiler error on Windows systems (Tested for VS 2017, 2019 and VS 2022):
1>xxx\include\fmt\core.h(1805,1): error C2968: 'is_formattable<std::filesystem::path,char>': recursive alias declaration
1>xxx\fmt-issue-2.cpp(11): message : see reference to class template instantiation 'std::is_constructible<fmt::v8::formatter<std::filesystem::path,char,void>>' being compiled
1>xxx\fmt-issue-2.cpp(9): message : see reference to alias template instantiation 'fmt::v8::is_formattable<std::filesystem::path,char>' being compiled
1>xxx\fmt-issue-2.cpp(11,9): error C2938: 'fmt::v8::is_formattable' : Failed to specialize alias template
1>xxx\fmt-issue-2.cpp(11): error C2062: type 'unknown-type' unexpected
1>xxx\fmt-issue-2.cpp(11,12): error C2039: 'value': is not a member of '`global namespace''
That means that the current mechanism relies on undefined compiler behaviour and we are lucky that any code using the current formatter for std::filesystem::path from fmt/std.h currently still compiles and does what it intends ;-)
By introducing an additionally suggested fmt::details::is_not_recursive_range trait this problem could be eliminated and would very likely also have the effect that we can enable the formatter for std::filesystem::path again for VS 2017 and below as well.
Given this explanation I'm volunteering to make a PULL request to realize this aim. Does this suggested approach sound?
Recently (via commit d2a2320), the existing
formatterspecialization for ranges has been made more robust by introduction of short-circuiting meta-programming logical functions. Even though this is an improvement in the existing code base that cannot rely on C++ language concepts, this is still not enough. This issue is opened to suggest to introduce a non-concept-based compile-time barrier against recursive range types (Such asstd::filesystem::pathorboost::filesystem::path), corresponding to the specialization suggested in the C++ proposal P2286R8.The reason for this need can be easily explained by observing that on Windows's systems
fmt::is_rangereturnstrueforchar, see https://godbolt.org/z/88bGdh14r (and on posix systems it returnstrueforwchar_t, see https://godbolt.org/z/8e5WEh45Y), that means that while the compiler attempts to find out whether the partial specialization infmt/include/fmt/std.h
Line 65 in c4ee726
fmt/include/fmt/ranges.h
Line 396 in c4ee726
the value for the
Enablerparameter is evaluated. Iffmt::is_rangereturnstrue, this means that it is guaranteed that also the additional part of the test,fmt/include/fmt/ranges.h
Lines 404 to 407 in c4ee726
is also evaluated and this can lead to an infinite compiler recursion, which can be observed, when attempting to evaluate that part of the expression in an isolated program (For the analysis, we can exclude the influence of the second part involving
detail::has_fallback_formatter, because this evaluates now by default to a constant value), such as the following one:which results in the following compiler error on Windows systems (Tested for VS 2017, 2019 and VS 2022):
That means that the current mechanism relies on undefined compiler behaviour and we are lucky that any code using the current
formatterforstd::filesystem::pathfromfmt/std.hcurrently still compiles and does what it intends ;-)By introducing an additionally suggested
fmt::details::is_not_recursive_rangetrait this problem could be eliminated and would very likely also have the effect that we can enable theformatterforstd::filesystem::pathagain for VS 2017 and below as well.Given this explanation I'm volunteering to make a PULL request to realize this aim. Does this suggested approach sound?