Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sstring: inherit publicly from string_view formatter #1855

Closed

Conversation

avikivity
Copy link
Member

@avikivity avikivity commented Oct 1, 2023

fmt 10 detects if a formatter provides some method (set_debug_format()),
but the detection fails if the method is private and compilation breaks
on clang [1].

Work around the clang bug by inheriting publicly.

[1] llvm/llvm-project#68849

A test case reproducing the problem is included (thanks
Kefu Chai kefu.chai@scylladb.com).

@@ -883,7 +883,7 @@ std::ostream& operator<<(std::ostream& os, const std::unordered_map<Key, T, Hash
SEASTAR_MODULE_EXPORT
template <typename char_type, typename Size, Size max_size, bool NulTerminate>
struct fmt::formatter<seastar::basic_sstring<char_type, Size, max_size, NulTerminate>>
: private fmt::formatter<std::basic_string_view<char_type>> {
: public fmt::formatter<std::basic_string_view<char_type>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i understand that ranges formatter uses maybe_set_debug_format() to optionally enables the debugging facilities when the formatting specifier is "?". and this helper function is specialized for the case where set_debug_format() is callable. see https://github.com/fmtlib/fmt/blob/f76603f21ea75bcd8d0007d7599cac75862b13b9/include/fmt/ranges.h#L287-L293

after reading the related code in fmtlib, i think our formatter should be correct, if it fails the build, it should be a bug in the compiler or fmtlib. but i cannot reproduce this failure with following patch:

diff --git a/cooking_recipe.cmake b/cooking_recipe.cmake
index 32e2b3a1..92e13efe 100644
--- a/cooking_recipe.cmake
+++ b/cooking_recipe.cmake
@@ -312,8 +312,7 @@ cooking_ingredient (dpdk
 
 cooking_ingredient (fmt
   EXTERNAL_PROJECT_ARGS
-    URL https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz
-    URL_MD5 21fac48cae8f3b4a5783ae06b443973a
+    URL https://github.com/fmtlib/fmt/archive/10.1.1.tar.gz
   CMAKE_ARGS
     -DFMT_DOC=OFF
     -DFMT_TEST=OFF)
diff --git a/tests/unit/sstring_test.cc b/tests/unit/sstring_test.cc
index 2cc78383..bcd6593e 100644
--- a/tests/unit/sstring_test.cc
+++ b/tests/unit/sstring_test.cc
@@ -24,6 +24,7 @@
 #include <boost/test/unit_test.hpp>
 #include <seastar/core/sstring.hh>
 #include <list>
+#include <fmt/ranges.h>
 
 using namespace std::literals;
 using namespace seastar;
@@ -306,3 +307,13 @@ BOOST_AUTO_TEST_CASE(test_compares_left_hand_not_string) {
     BOOST_REQUIRE(std::string("a") < sstring("b"));
 #endif
 }
+
+#if FMT_VERSION >= 90000
+
+BOOST_AUTO_TEST_CASE(test_range_fmt) {
+    std::ignore = fmt::format("{}", sstring{"hello"});
+    std::vector<sstring> strings;
+    std::ignore = fmt::format("{}", strings);
+}
+
+#endif

i tested with clang 16.0.6, and fmtlib 10.1.1. the building system is generated using

$ ./configure.py --cook fmt --compiler clang++ --c-compiler clang --mode debug --disable-dpdk

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try to extract a reproducer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FAILED: build/dev/test/unit/row_cache_stress_test.o 
clang++ -MD -MT build/dev/test/unit/row_cache_stress_test.o -MF build/dev/test/unit/row_cache_stress_test.o.d -I/home/avi/scylla/seastar/include -I/home/avi/scylla/build/dev/seastar/gen/include -U_FORTIFY_SOURCE -Werror=unused-result -fstack-clash-protection -DSEASTAR_API_LEVEL=7 -DSEASTAR_BUILD_SHARED_LIBS -DSEASTAR_SSTRING -DSEASTAR_ENABLE_ALLOC_FAILURE_INJECTION -DSEASTAR_SCHEDULING_GROUPS_COUNT=16 -DSEASTAR_LOGGER_TYPE_STDOUT -DSEASTAR_TYPE_ERASE_MORE -DFMT_SHARED -I/usr/include/p11-kit-1   -ffile-prefix-map=/home/avi/scylla=. -march=westmere -DDEVEL -DSEASTAR_ENABLE_ALLOC_FAILURE_INJECTION -DSCYLLA_ENABLE_ERROR_INJECTION -O2 -DSCYLLA_BUILD_MODE=dev -iquote. -iquote build/dev/gen --std=gnu++20  -ffile-prefix-map=/home/avi/scylla=. -march=westmere  -DBOOST_TEST_DYN_LINK   -DNOMINMAX -DNOMINMAX -fvisibility=hidden -Wall -Werror -Wimplicit-fallthrough -Wno-mismatched-tags -Wno-c++11-narrowing -Wno-overloaded-virtual -Wno-unused-command-line-argument -Wno-unsupported-friend -Wno-implicit-int-float-conversion -Wno-psabi -Wno-narrowing -Wno-error=deprecated-declarations -DXXH_PRIVATE_API -DSEASTAR_TESTING_MAIN  -c -o build/dev/test/unit/row_cache_stress_test.o test/unit/row_cache_stress_test.cc
In file included from test/unit/row_cache_stress_test.cc:22:
/usr/include/fmt/std.h:122:7: error: 'set_debug_format' is a private member of 'fmt::formatter<fmt::basic_string_view<char>>'
  122 |     u.set_debug_format(set);
      |     ~~^~~~~~~~~~~~~~~~
/usr/include/fmt/std.h:130:5: note: in instantiation of function template specialization 'fmt::formatter<std::optional<seastar::basic_sstring<char, unsigned int, 15>>>::maybe_set_debug_format<fmt::formatter<seastar::basic_sstring<char, unsigned int, 15>>>' requested here
  130 |     maybe_set_debug_format(underlying_, true);
      |     ^
/usr/include/fmt/core.h:1297:28: note: in instantiation of function template specialization 'fmt::formatter<std::optional<seastar::basic_sstring<char, unsigned int, 15>>>::parse<fmt::basic_format_parse_context<char>>' requested here
 1297 |     parse_ctx.advance_to(f.parse(parse_ctx));
      |                            ^
/usr/include/fmt/core.h:1283:21: note: in instantiation of function template specialization 'fmt::detail::value<fmt::basic_format_context<fmt::appender, char>>::format_custom_arg<std::optional<seastar::basic_sstring<char, unsigned int, 15>>, fmt::formatter<std::optional<seastar::basic_sstring<char, unsigned int, 15>>>>' requested here
 1283 |     custom.format = format_custom_arg<
      |                     ^
/home/avi/scylla/seastar/include/seastar/core/print.hh:145:10: note: in instantiation of function template specialization 'fmt::format_to<fmt::appender, unsigned long &, std::optional<seastar::basic_sstring<char, unsigned int, 15>> &, seastar::basic_sstring<char, unsigned int, 15> &, 0>' requested here
  145 |     fmt::format_to(fmt::appender(out), fmt::runtime(fmt), std::forward<A>(a)...);
      |          ^
test/unit/row_cache_stress_test.cc:228:38: note: in instantiation of function template specialization 'seastar::format<unsigned long &, std::optional<seastar::basic_sstring<char, unsigned int, 15>> &, seastar::basic_sstring<char, unsigned int, 15> &>' requested here
  228 |             throw std::runtime_error(format("Saw values from two different writes in partition {:d}: {} and {}", _key, _value, value));
      |                                      ^
/home/avi/scylla/seastar/include/seastar/core/sstring.hh:886:7: note: constrained by private inheritance here
  886 |     : private fmt::formatter<std::basic_string_view<char_type>> {
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/fmt/core.h:2715:22: note: member is declared here
 2715 |   FMT_CONSTEXPR void set_debug_format(bool set = true) {
      |                      ^
1 error generated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working reproducer:

diff --git a/tests/unit/sstring_test.cc b/tests/unit/sstring_test.cc
index 2cc7838389..7b94d95b02 100644
--- a/tests/unit/sstring_test.cc
+++ b/tests/unit/sstring_test.cc
@@ -22,10 +22,12 @@
 #define BOOST_TEST_MODULE core
 
 #include <boost/test/unit_test.hpp>
 #include <seastar/core/sstring.hh>
 #include <list>
+#include <fmt/ranges.h>
+#include <fmt/std.h>
 
 using namespace std::literals;
 using namespace seastar;
 
 BOOST_AUTO_TEST_CASE(test_make_sstring) {
@@ -304,5 +306,15 @@ BOOST_AUTO_TEST_CASE(test_compares_left_hand_not_string) {
 #ifdef __cpp_lib_three_way_comparison
     BOOST_REQUIRE("a" < sstring("b"));
     BOOST_REQUIRE(std::string("a") < sstring("b"));
 #endif
 }
+
+#if FMT_VERSION >= 90000
+
+BOOST_AUTO_TEST_CASE(test_range_fmt) {
+    std::ignore = fmt::format("{}", std::optional(sstring{"hello"}));
+    std::vector<sstring> strings;
+    std::ignore = fmt::format("{}", strings);
+}
+
+#endif
diff --git a/cooking_recipe.cmake b/cooking_recipe.cmake
index 32e2b3a17a..1789664d93 100644
--- a/cooking_recipe.cmake
+++ b/cooking_recipe.cmake
@@ -310,12 +310,11 @@ cooking_ingredient (dpdk
     INSTALL_COMMAND
       ${Ninja_EXECUTABLE} -C <BINARY_DIR> install)
 
 cooking_ingredient (fmt
   EXTERNAL_PROJECT_ARGS
-    URL https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz
-    URL_MD5 21fac48cae8f3b4a5783ae06b443973a
+    URL https://github.com/fmtlib/fmt/archive/10.1.1.tar.gz
   CMAKE_ARGS
     -DFMT_DOC=OFF
     -DFMT_TEST=OFF)
 
 cooking_ingredient (liburing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sstring works, but optional` failed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see. it is a clang bug. see https://godbolt.org/z/4GMdKb9en . still working on a minimal reproducer, so i can report it upstream.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great, thanks.

@avikivity avikivity force-pushed the sstring-formatter-not-private branch from 9d102dd to 5794968 Compare October 8, 2023 14:07
@avikivity
Copy link
Member Author

v2: add test case


#if FMT_VERSION >= 90000

BOOST_AUTO_TEST_CASE(test_range_fmt) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
BOOST_AUTO_TEST_CASE(test_range_fmt) {
BOOST_AUTO_TEST_CASE(test_fmt) {

as it tests both range and std::optional<sstring>.

@@ -883,7 +883,7 @@ std::ostream& operator<<(std::ostream& os, const std::unordered_map<Key, T, Hash
SEASTAR_MODULE_EXPORT
template <typename char_type, typename Size, Size max_size, bool NulTerminate>
struct fmt::formatter<seastar::basic_sstring<char_type, Size, max_size, NulTerminate>>
: private fmt::formatter<std::basic_string_view<char_type>> {
: public fmt::formatter<std::basic_string_view<char_type>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see. it is a clang bug. see https://godbolt.org/z/4GMdKb9en . still working on a minimal reproducer, so i can report it upstream.

@@ -883,7 +883,7 @@ std::ostream& operator<<(std::ostream& os, const std::unordered_map<Key, T, Hash
SEASTAR_MODULE_EXPORT
template <typename char_type, typename Size, Size max_size, bool NulTerminate>
struct fmt::formatter<seastar::basic_sstring<char_type, Size, max_size, NulTerminate>>
: private fmt::formatter<std::basic_string_view<char_type>> {
: public fmt::formatter<std::basic_string_view<char_type>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason why i chose to use private inheritance was to avoid the disputations based on Liskov principal (the IS-A principal). but if we need to workaround this compiler bug. i think i'd give up and just use the public inheritance, as it's simpler:

  1. drop private.
  2. drop using base::parse
  3. optionally replace base with fmt::formatter<format_as_t>. as it is not reused.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that private is better here, I switched to public to make it work, we can add a comment referencing the clang bug once we know what it is.

@tchaikov
Copy link
Contributor

also, might want to s/strig_view/string_view/ in the title of the commit message.

#if FMT_VERSION >= 90000

BOOST_AUTO_TEST_CASE(test_range_fmt) {
std::ignore = fmt::format("{}", std::optional(sstring{"hello"}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might want to guard this very test with #if FMT_VERSION >= 100000, and keep the test for fmt/range in #if FMT_VERSION >= 90000. as the formatter for std::optional<> as introduced by fmtlib v10.0.0, see https://github.com/fmtlib/fmt/releases/tag/10.0.0.

this is why the CI testing is failing for this PR -- we only have access to fmt-devel 9.1.0 in the frozen toolchain docker container.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack

@avikivity avikivity force-pushed the sstring-formatter-not-private branch from 5794968 to e4cd64b Compare October 17, 2023 11:14
@avikivity
Copy link
Member Author

v3:

  • fixed spelling in commitlog
  • reworded commitlog to point to clang bug
  • added comments referencing clang bug
  • only test std::optional formatting with fmt 10

@avikivity avikivity changed the title sstring: inherit publicly from strig_view formatter sstring: inherit publicly from sstring_view formatter Oct 17, 2023
Copy link
Contributor

@tchaikov tchaikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm. could you reconsider the 2nd item in https://github.com/scylladb/seastar/pull/1855/files#r1354315456 . as using base::parse is not necessary anymore.

@avikivity
Copy link
Member Author

v5: drop now unnecessary using base::parse

@avikivity avikivity force-pushed the sstring-formatter-not-private branch from e4cd64b to 6304e0e Compare October 17, 2023 12:45
Copy link
Contributor

@tchaikov tchaikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks. still lgtm

@tchaikov
Copy link
Contributor

just a nit: s/sstring_view/string_view/

@avikivity avikivity changed the title sstring: inherit publicly from sstring_view formatter sstring: inherit publicly from string_view formatter Oct 18, 2023
fmt 10 detects if a formatter provides some method (set_debug_format()),
but the detection fails if the method is private and compilation breaks
on clang [1].

Work around the clang bug by inheriting publicly.

[1] llvm/llvm-project#68849

A test case reproducing the problem is included (thanks
Kefu Chai <kefu.chai@scylladb.com>).
@avikivity avikivity force-pushed the sstring-formatter-not-private branch from 6304e0e to 2a4a7b1 Compare October 18, 2023 10:08
@avikivity
Copy link
Member Author

v6: s/s(string_view)/\1/

Copy link
Contributor

@tchaikov tchaikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm.

@avikivity
Copy link
Member Author

@nyh please review

@avikivity avikivity requested a review from nyh October 19, 2023 08:53
@nyh nyh closed this in e6a848b Oct 19, 2023
graphcareful pushed a commit to graphcareful/seastar that referenced this pull request Mar 20, 2024
fmt 10 detects if a formatter provides some method (set_debug_format()),
but the detection fails if the method is private and compilation breaks
on clang [1].

Work around the clang bug by inheriting publicly.

[1] llvm/llvm-project#68849

A test case reproducing the problem is included (thanks
Kefu Chai <kefu.chai@scylladb.com>).

Closes scylladb#1855
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants