From e91e92761aa31d6d493297763cd9f77a361aaa2d Mon Sep 17 00:00:00 2001 From: Tiago Date: Sat, 12 Sep 2026 07:14:49 -0700 Subject: [PATCH] Don't let ADL pick to_string_view (#4938) * Don't let ADL pick to_string_view 02bf4d1c disabled ADL for to_string_view by qualifying the is_string trait, and has_to_string_view and char_t are qualified for the same reason. Two call sites were left behind: value's string constructor and the write() overload for types with a string view conversion. Both sit inside fmt::detail, where an unqualified call adds the argument's own namespace to the overload set. That splits the two halves of one decision. Both call sites are reached only through has_to_string_view or char_t, which are defined by the qualified expression, so ADL can never be needed to satisfy them - it can only add candidates the gate never considered. When the argument's namespace declares a to_string_view template, the two tie during partial ordering and the call is ambiguous: core.h(2211): error C2668: 'to_string_view': ambiguous call to overloaded function note: could be 'string_view N::to_string_view(const T&)' [found using argument-dependent lookup] note: or 'basic_string_view fmt::detail::to_string_view(const T&)' Both are reachable. format("{}", x) stores the argument through value's constructor; to_string(x) passes it to detail::write unmapped, which lands on the write() overload, as do FMT_COMPILE named fields and nested_formatter::write_arg. Reverting either line alone breaks the build of the test that covers it. This turned up in Microsoft Office, which declares a constrained to_string_view template next to its own string types. It only breaks where those types are distinct classes, so the same code compiles on platforms whose string types are std aliases - the ADL set is namespace std there and picks up nothing. One behaviour change worth noting: a non-template to_string_view in the argument's namespace used to win outright at these call sites, so a type that satisfies is_std_string_like via find_first_of and data() but has no size() formatted through ADL and now fails to compile, because the trait only checks that the qualified overload is viable, not that its body instantiates. That is the removed extension point going away rather than a new restriction; ADL to_string_view stopped being supported in 02bf4d1c. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- include/fmt/core.h | 2 +- include/fmt/format.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 8d36f75f..f0815206 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -2208,7 +2208,7 @@ template class value { FMT_CONSTEXPR value(const T& x FMT_BUILTIN) { static_assert(std::is_same::value, "mixing character types is disallowed"); - auto sv = to_string_view(x); + auto sv = detail::to_string_view(x); string.data = sv.data(); string.size = sv.size(); } diff --git a/include/fmt/format.h b/include/fmt/format.h index 318ce814..96aa20ed 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3815,7 +3815,7 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) template ::value)> constexpr auto write(OutputIt out, const T& value) -> OutputIt { - return write(out, to_string_view(value)); + return write(out, detail::to_string_view(value)); } // FMT_ENABLE_IF() condition separated to workaround an MSVC bug.